X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fscriptapi.cpp;h=ec3d34676672decfdcca5db0d2a5f9b7665bd7f2;hb=318669327e84d8fc7498e32c5bacfad658d8ba87;hp=27ab2950be7c404446e605cc79f8f967d97f270d;hpb=2e90ed07acd295387c0da6c0689d14665b6c125d;p=oweals%2Fminetest.git diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 27ab2950b..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. */ @@ -33,20 +33,23 @@ extern "C" { #include "filesys.h" #include "serverobject.h" #include "script.h" -//#include "luna.h" -#include "luaentity_common.h" -#include "content_sao.h" // For LuaEntitySAO +#include "object_properties.h" +#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) @@ -647,6 +715,27 @@ static void read_groups(lua_State *L, int index, } } +/* + Privileges +*/ +static void read_privileges(lua_State *L, int index, + std::set &result) +{ + result.clear(); + lua_pushnil(L); + if(index < 0) + index -= 1; + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + std::string key = luaL_checkstring(L, -2); + bool value = lua_toboolean(L, -1); + if(value) + result.insert(key); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } +} + /* ToolCapabilities */ @@ -669,8 +758,19 @@ static ToolCapabilities read_tool_capabilities( // This will be created ToolGroupCap groupcap; // Read simple parameters - getfloatfield(L, table_groupcap, "maxwear", groupcap.maxwear); getintfield(L, table_groupcap, "maxlevel", groupcap.maxlevel); + getintfield(L, table_groupcap, "uses", groupcap.uses); + // DEPRECATED: maxwear + float maxwear = 0; + if(getfloatfield(L, table_groupcap, "maxwear", maxwear)){ + if(maxwear != 0) + groupcap.uses = 1.0/maxwear; + else + groupcap.uses = 0; + infostream<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); + + lua_getfield(L, -1, "visual_size"); + if(lua_istable(L, -1)) + prop->visual_size = read_v2f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "textures"); + if(lua_istable(L, -1)){ + prop->textures.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->textures.push_back(lua_tostring(L, -1)); + else + prop->textures.push_back(""); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + 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)) + prop->spritediv = read_v2s16(L, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "initial_sprite_basepos"); + 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); +} + /* 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); @@ -844,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( @@ -862,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 */ @@ -875,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); @@ -889,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++; @@ -910,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++; @@ -947,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); @@ -969,7 +1237,9 @@ static ContentFeatures read_content_features(lua_State *L, int index) "deprecated: use 'drop' field"); warn_if_field_exists(L, index, "extra_dug_item_rarity", "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); @@ -985,8 +1255,6 @@ static ContentFeatures read_content_features(lua_State *L, int index) getboolfield(L, index, "climbable", f.climbable); // Player can build on these getboolfield(L, index, "buildable_to", f.buildable_to); - // Metadata name of node (eg. "furnace") - getstringfield(L, index, "metadata_name", f.metadata_name); // Whether the node is non-liquid, source liquid or flowing liquid f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", es_LiquidType, LIQUID_NONE); @@ -1001,44 +1269,43 @@ 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, "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.type = (NodeBoxType)getenumfield(L, -1, "type", - es_NodeBoxType, NODEBOX_REGULAR); + if(lua_istable(L, -1)) + f.selection_box = read_nodebox(L, -1); + lua_pop(L, 1); - lua_getfield(L, -1, "fixed"); - if(lua_istable(L, -1)) - f.selection_box.fixed = read_aabbox3df32(L, -1, BS); - 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); - lua_getfield(L, -1, "wall_top"); - if(lua_istable(L, -1)) - f.selection_box.wall_top = read_aabbox3df32(L, -1, BS); + // Sound table + lua_getfield(L, index, "sounds"); + if(lua_istable(L, -1)){ + lua_getfield(L, -1, "footstep"); + read_soundspec(L, -1, f.sound_footstep); 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_getfield(L, -1, "dig"); + read_soundspec(L, -1, f.sound_dig); 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_getfield(L, -1, "dug"); + read_soundspec(L, -1, f.sound_dug); lua_pop(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); - return f; } @@ -1047,11 +1314,13 @@ 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) { - dstream<<"inventory_set_list_from_lua\n"; if(tableindex < 0) tableindex = lua_gettop(L) + 1 + tableindex; // If nil, delete list @@ -1060,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; @@ -1083,7 +1344,6 @@ static void inventory_set_list_from_lua(Inventory *inv, const char *name, invlist->deleteItem(index); index++; } - dstream<<"inventory_set_list_from_lua done\n"; } static void inventory_get_list_to_lua(Inventory *inv, const char *name, @@ -1094,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); } /* @@ -1130,7 +1379,7 @@ private: static const luaL_reg methods[]; // Exported functions - + // garbage collector static int gc_object(lua_State *L) { @@ -1346,7 +1595,7 @@ private: ItemStack &item = o->m_stack; u32 takecount = 1; if(!lua_isnone(L, 2)) - takecount = lua_tointeger(L, 2); + takecount = luaL_checkinteger(L, 2); ItemStack taken = item.takeItem(takecount); create(L, taken); return 1; @@ -1383,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) @@ -1514,29 +1763,68 @@ static ItemStack read_item(lua_State *L, int index) } } -/* - InvRef -*/ - -class InvRef +static std::vector read_items(lua_State *L, int index) { -private: - InventoryLocation m_loc; - - static const char className[]; - static const luaL_reg methods[]; + if(index < 0) + index = lua_gettop(L) + 1 + index; - static InvRef *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 *(InvRef**)ud; // unbox pointer + 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); } - - static Inventory* getinv(lua_State *L, InvRef *ref) - { - return get_server(L)->getInventory(ref->m_loc); + 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); } static InventoryList* getlist(lua_State *L, InvRef *ref, @@ -1553,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)); @@ -1563,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) { @@ -1577,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) { @@ -1599,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) { @@ -1727,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) @@ -1789,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), @@ -1799,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} }; @@ -1822,25 +2196,21 @@ private: if(!ud) luaL_typerror(L, narg, className); return *(NodeMetaRef**)ud; // unbox pointer } - - static NodeMetadata* getmeta(NodeMetaRef *ref) + + static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create) { NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p); + if(meta == NULL && auto_create) + { + meta = new NodeMetadata(ref->m_env->getGameDef()); + ref->m_env->getMap().setNodeMetadata(ref->m_p, meta); + } return meta; } - /*static IGenericNodeMetadata* getgenericmeta(NodeMetaRef *ref) - { - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) - return NULL; - if(meta->typeId() != NODEMETA_GENERIC) - return NULL; - return (IGenericNodeMetadata*)meta; - }*/ - 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; @@ -1853,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)); @@ -1863,106 +2233,99 @@ private: return 0; } - // get_type(self) - static int l_get_type(lua_State *L) + // get_string(self, name) + static int l_get_string(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); + std::string name = luaL_checkstring(L, 2); + + NodeMetadata *meta = getmeta(ref, false); if(meta == NULL){ - lua_pushnil(L); + lua_pushlstring(L, "", 0); return 1; } - // Do it - lua_pushstring(L, meta->typeName()); + std::string str = meta->getString(name); + lua_pushlstring(L, str.c_str(), str.size()); return 1; } - // allows_text_input(self) - static int l_allows_text_input(lua_State *L) + // set_string(self, name, var) + static int l_set_string(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - lua_pushboolean(L, meta->allowsTextInput()); - return 1; - } + std::string name = luaL_checkstring(L, 2); + size_t len = 0; + const char *s = lua_tolstring(L, 3, &len); + std::string str(s, len); - // set_text(self, text) - static int l_set_text(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - std::string text = luaL_checkstring(L, 2); - meta->setText(text); + NodeMetadata *meta = getmeta(ref, !str.empty()); + if(meta == NULL || str == meta->getString(name)) + return 0; + meta->setString(name, str); reportMetadataChange(ref); return 0; } - // get_text(self) - static int l_get_text(lua_State *L) + // get_int(self, name) + static int l_get_int(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - std::string text = meta->getText(); - lua_pushstring(L, text.c_str()); - return 1; - } + std::string name = lua_tostring(L, 2); - // get_owner(self) - static int l_get_owner(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - std::string owner = meta->getOwner(); - lua_pushstring(L, owner.c_str()); + NodeMetadata *meta = getmeta(ref, false); + if(meta == NULL){ + lua_pushnumber(L, 0); + return 1; + } + std::string str = meta->getString(name); + lua_pushnumber(L, stoi(str)); return 1; } - // set_owner(self, string) - static int l_set_owner(lua_State *L) + // set_int(self, name, var) + static int l_set_int(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - std::string owner = luaL_checkstring(L, 2); - meta->setOwner(owner); + std::string name = lua_tostring(L, 2); + int a = lua_tointeger(L, 3); + std::string str = itos(a); + + NodeMetadata *meta = getmeta(ref, true); + if(meta == NULL || str == meta->getString(name)) + return 0; + meta->setString(name, str); reportMetadataChange(ref); - return 1; + return 0; } - // get_allow_removal(self) - static int l_get_allow_removal(lua_State *L) + // get_float(self, name) + static int l_get_float(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); + std::string name = lua_tostring(L, 2); + + NodeMetadata *meta = getmeta(ref, false); if(meta == NULL){ - lua_pushboolean(L, true); + lua_pushnumber(L, 0); return 1; } - // Do it - lua_pushboolean(L, !meta->nodeRemovalDisabled()); + std::string str = meta->getString(name); + lua_pushnumber(L, stof(str)); return 1; } - /* IGenericNodeMetadata interface */ - - // set_infotext(self, text) - static int l_set_infotext(lua_State *L) + // set_float(self, name, var) + static int l_set_float(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - std::string text = luaL_checkstring(L, 2); - meta->setInfoText(text); + std::string name = lua_tostring(L, 2); + float a = lua_tonumber(L, 3); + std::string str = ftos(a); + + NodeMetadata *meta = getmeta(ref, true); + if(meta == NULL || str == meta->getString(name)) + return 0; + meta->setString(name, str); reportMetadataChange(ref); return 0; } @@ -1971,137 +2334,94 @@ private: static int l_get_inventory(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it + getmeta(ref, true); // try to ensure the metadata exists InvRef::createNodeMeta(L, ref->m_p); return 1; } - // set_inventory_draw_spec(self, text) - static int l_set_inventory_draw_spec(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - std::string text = luaL_checkstring(L, 2); - meta->setInventoryDrawSpec(text); - reportMetadataChange(ref); - return 0; - } - - // set_allow_text_input(self, text) - static int l_set_allow_text_input(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - bool b = lua_toboolean(L, 2); - meta->setAllowTextInput(b); - reportMetadataChange(ref); - return 0; - } - - // set_allow_removal(self, text) - static int l_set_allow_removal(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - bool b = lua_toboolean(L, 2); - meta->setRemovalDisabled(!b); - reportMetadataChange(ref); - return 0; - } - - // set_enforce_owner(self, text) - static int l_set_enforce_owner(lua_State *L) + // to_table(self) + static int l_to_table(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - bool b = lua_toboolean(L, 2); - meta->setEnforceOwner(b); - reportMetadataChange(ref); - return 0; - } - // is_inventory_modified(self) - static int l_is_inventory_modified(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - lua_pushboolean(L, meta->isInventoryModified()); + NodeMetadata *meta = getmeta(ref, true); + if(meta == NULL){ + lua_pushnil(L); + 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); + } + } + 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; } - // reset_inventory_modified(self) - static int l_reset_inventory_modified(lua_State *L) + // from_table(self, table) + static int l_from_table(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - meta->resetInventoryModified(); - reportMetadataChange(ref); - return 0; - } - - // is_text_modified(self) - static int l_is_text_modified(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - lua_pushboolean(L, meta->isTextModified()); - return 1; - } + int base = 2; - // reset_text_modified(self) - static int l_reset_text_modified(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - meta->resetTextModified(); - reportMetadataChange(ref); - return 0; - } + if(lua_isnil(L, base)){ + // No metadata + ref->m_env->getMap().removeNodeMetadata(ref->m_p); + lua_pushboolean(L, true); + return 1; + } - // set_string(self, name, var) - static int l_set_string(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - std::string name = luaL_checkstring(L, 2); - size_t len = 0; - const char *s = lua_tolstring(L, 3, &len); - std::string str(s, len); - meta->setString(name, 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; - } - - // get_string(self, name) - static int l_get_string(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; - // Do it - std::string name = luaL_checkstring(L, 2); - std::string str = meta->getString(name); - lua_pushlstring(L, str.c_str(), str.size()); + lua_pushboolean(L, true); return 1; } @@ -2157,25 +2477,15 @@ public: }; const char NodeMetaRef::className[] = "NodeMetaRef"; const luaL_reg NodeMetaRef::methods[] = { - method(NodeMetaRef, get_type), - method(NodeMetaRef, allows_text_input), - method(NodeMetaRef, set_text), - method(NodeMetaRef, get_text), - method(NodeMetaRef, get_owner), - method(NodeMetaRef, set_owner), - method(NodeMetaRef, get_allow_removal), - method(NodeMetaRef, set_infotext), - method(NodeMetaRef, get_inventory), - method(NodeMetaRef, set_inventory_draw_spec), - method(NodeMetaRef, set_allow_text_input), - method(NodeMetaRef, set_allow_removal), - method(NodeMetaRef, set_enforce_owner), - method(NodeMetaRef, is_inventory_modified), - method(NodeMetaRef, reset_inventory_modified), - method(NodeMetaRef, is_text_modified), - method(NodeMetaRef, reset_text_modified), - method(NodeMetaRef, set_string), method(NodeMetaRef, get_string), + method(NodeMetaRef, set_string), + method(NodeMetaRef, get_int), + method(NodeMetaRef, set_int), + method(NodeMetaRef, get_float), + method(NodeMetaRef, set_float), + method(NodeMetaRef, get_inventory), + method(NodeMetaRef, to_table), + method(NodeMetaRef, from_table), {0,0} }; @@ -2190,7 +2500,7 @@ private: static const char className[]; static const luaL_reg methods[]; - +public: static ObjectRef *checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); @@ -2198,13 +2508,13 @@ private: if(!ud) luaL_typerror(L, narg, className); return *(ObjectRef**)ud; // unbox pointer } - + static ServerActiveObject* getobject(ObjectRef *ref) { ServerActiveObject *co = ref->m_object; return co; } - +private: static LuaEntitySAO* getluaobject(ObjectRef *ref) { ServerActiveObject *obj = getobject(ref); @@ -2214,19 +2524,27 @@ private: return NULL; return (LuaEntitySAO*)obj; } - - static ServerRemotePlayer* getplayer(ObjectRef *ref) + + static PlayerSAO* getplayersao(ObjectRef *ref) { ServerActiveObject *obj = getobject(ref); if(obj == NULL) return NULL; if(obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) return NULL; - return static_cast(obj); + return (PlayerSAO*)obj; } - + + static Player* getplayer(ObjectRef *ref) + { + PlayerSAO *playersao = getplayersao(ref); + if(playersao == NULL) + return NULL; + return playersao->getPlayer(); + } + // Exported functions - + // garbage collector static int gc_object(lua_State *L) { ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1)); @@ -2245,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) @@ -2263,7 +2581,7 @@ private: lua_setfield(L, -2, "z"); return 1; } - + // setpos(self, pos) static int l_setpos(lua_State *L) { @@ -2275,13 +2593,9 @@ private: v3f pos = checkFloatPos(L, 2); // Do it co->setPos(pos); - // Move player if applicable - ServerRemotePlayer *player = getplayer(ref); - if(player != NULL) - get_server(L)->SendMovePlayer(player); return 0; } - + // moveto(self, pos, continuous=false) static int l_moveto(lua_State *L) { @@ -2298,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); @@ -2307,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; } @@ -2356,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; } @@ -2407,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; @@ -2426,6 +2753,107 @@ private: return 1; } + // set_armor_groups(self, groups) + static int l_set_armor_groups(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + ItemGroupList groups; + read_groups(L, 2, groups); + co->setArmorGroups(groups); + 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) + { + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + ObjectProperties *prop = co->accessObjectProperties(); + if(!prop) + return 0; + read_object_properties(L, 2, prop); + co->notifyObjectPropertiesModified(); + return 0; + } + /* LuaEntitySAO-only */ // setvelocity(self, {x=num, y=num, z=num}) @@ -2434,13 +2862,12 @@ private: ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; - // pos v3f pos = checkFloatPos(L, 2); // Do it co->setVelocity(pos); return 0; } - + // getvelocity(self) static int l_getvelocity(lua_State *L) { @@ -2452,7 +2879,7 @@ private: pushFloatPos(L, v); return 1; } - + // setacceleration(self, {x=num, y=num, z=num}) static int l_setacceleration(lua_State *L) { @@ -2465,7 +2892,7 @@ private: co->setAcceleration(pos); return 0; } - + // getacceleration(self) static int l_getacceleration(lua_State *L) { @@ -2477,20 +2904,19 @@ private: pushFloatPos(L, v); return 1; } - + // setyaw(self, radians) static int l_setyaw(lua_State *L) { ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; - // pos float yaw = luaL_checknumber(L, 2) * core::RADTODEG; // Do it co->setYaw(yaw); return 0; } - + // getyaw(self) static int l_getyaw(lua_State *L) { @@ -2502,7 +2928,7 @@ private: lua_pushnumber(L, yaw); return 1; } - + // settexturemod(self, mod) static int l_settexturemod(lua_State *L) { @@ -2514,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) @@ -2539,19 +2965,6 @@ private: return 0; } - // set_armor_groups(self, groups) - static int l_set_armor_groups(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - ItemGroupList groups; - read_groups(L, 2, groups); - co->setArmorGroups(groups); - return 0; - } - // DEPRECATED // get_entity_name(self) static int l_get_entity_name(lua_State *L) @@ -2564,7 +2977,7 @@ private: lua_pushstring(L, name.c_str()); return 1; } - + // get_luaentity(self) static int l_get_luaentity(lua_State *L) { @@ -2575,28 +2988,37 @@ 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); - ServerRemotePlayer *player = getplayer(ref); + 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) { ObjectRef *ref = checkobject(L, 1); - ServerRemotePlayer *player = getplayer(ref); + Player *player = getplayer(ref); if(player == NULL) return 0; // Do it float pitch = player->getRadPitch(); @@ -2610,7 +3032,7 @@ private: static int l_get_look_pitch(lua_State *L) { ObjectRef *ref = checkobject(L, 1); - ServerRemotePlayer *player = getplayer(ref); + Player *player = getplayer(ref); if(player == NULL) return 0; // Do it lua_pushnumber(L, player->getRadPitch()); @@ -2621,13 +3043,86 @@ private: static int l_get_look_yaw(lua_State *L) { ObjectRef *ref = checkobject(L, 1); - ServerRemotePlayer *player = getplayer(ref); + Player *player = getplayer(ref); if(player == NULL) return 0; // Do it lua_pushnumber(L, player->getRadYaw()); 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) @@ -2660,7 +3155,7 @@ public: ObjectRef *o = checkobject(L, -1); o->m_object = NULL; } - + static void Register(lua_State *L) { lua_newtable(L); @@ -2705,6 +3200,12 @@ const luaL_reg ObjectRef::methods[] = { method(ObjectRef, get_wield_index), 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), method(ObjectRef, getvelocity), @@ -2714,336 +3215,252 @@ const luaL_reg ObjectRef::methods[] = { method(ObjectRef, getyaw), method(ObjectRef, settexturemod), method(ObjectRef, setsprite), - method(ObjectRef, set_armor_groups), 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()); } } + /* - EnvRef -*/ + PerlinNoise + */ -class EnvRef +class LuaPerlinNoise { private: - ServerEnvironment *m_env; - + int seed; + int octaves; + float persistence; + float scale; static const char className[]; static const luaL_reg methods[]; - static EnvRef *checkobject(lua_State *L, int narg) + // Exported functions + + // garbage collector + static int gc_object(lua_State *L) { - 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 + LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1)); + delete o; + return 0; } - - // Exported functions - // EnvRef:add_node(pos, node) - // pos = {x=num, y=num, z=num} - static int l_add_node(lua_State *L) + static int l_get2d(lua_State *L) { - //infostream<<"EnvRef::l_add_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 - bool succeeded = env->getMap().addNodeWithEvent(pos, n); - lua_pushboolean(L, succeeded); + LuaPerlinNoise *o = checkobject(L, 1); + v2f pos2d = read_v2f(L,2); + lua_Number val = noise2d_perlin(pos2d.X/o->scale, pos2d.Y/o->scale, o->seed, o->octaves, o->persistence); + lua_pushnumber(L, val); return 1; } - - // EnvRef:remove_node(pos) - // pos = {x=num, y=num, z=num} - static int l_remove_node(lua_State *L) + static int l_get3d(lua_State *L) { - //infostream<<"EnvRef::l_remove_node()"<m_env; - if(env == NULL) return 0; - // pos - v3s16 pos = read_v3s16(L, 2); - // Do it - bool succeeded = env->getMap().removeNodeWithEvent(pos); - lua_pushboolean(L, succeeded); + LuaPerlinNoise *o = checkobject(L, 1); + v3f pos3d = read_v3f(L,2); + lua_Number val = noise3d_perlin(pos3d.X/o->scale, pos3d.Y/o->scale, pos3d.Z/o->scale, o->seed, o->octaves, o->persistence); + lua_pushnumber(L, val); return 1; } - // EnvRef:get_node(pos) - // pos = {x=num, y=num, z=num} - static int l_get_node(lua_State *L) +public: + LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence, + float a_scale): + seed(a_seed), + octaves(a_octaves), + persistence(a_persistence), + scale(a_scale) { - //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()); - 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) + ~LuaPerlinNoise() { - //infostream<<"EnvRef::l_get_node()"<m_env; - if(env == NULL) return 0; - // pos - v3s16 pos = read_v3s16(L, 2); - // Do it - try{ - MapNode n = env->getMap().getNode(pos); - // Return node - pushnode(L, n, env->getGameDef()->ndef()); - return 1; - } catch(InvalidPositionException &e) - { - lua_pushnil(L); - return 1; - } } - // EnvRef:get_node_light(pos, timeofday) - // pos = {x=num, y=num, z=num} - // timeofday: nil = current time, 0 = night, 0.5 = day - static int l_get_node_light(lua_State *L) + // LuaPerlinNoise(seed, octaves, persistence, scale) + // Creates an LuaPerlinNoise and leaves it on top of stack + static int create_object(lua_State *L) { - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - // Do it - v3s16 pos = read_v3s16(L, 2); - u32 time_of_day = env->getTimeOfDay(); - 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); - MapNode n = env->getMap().getNodeNoEx(pos); - try{ - MapNode n = env->getMap().getNode(pos); - INodeDefManager *ndef = env->getGameDef()->ndef(); - lua_pushinteger(L, n.getLightBlend(dnr, ndef)); - return 1; - } catch(InvalidPositionException &e) - { - lua_pushnil(L); - return 1; - } + int seed = luaL_checkint(L, 1); + int octaves = luaL_checkint(L, 2); + 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); + lua_setmetatable(L, -2); + 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) + static LuaPerlinNoise* checkobject(lua_State *L, int narg) { - //infostream<<"EnvRef::l_add_entity()"<m_env; - if(env == NULL) return 0; - // pos - v3f pos = checkFloatPos(L, 2); - // content - const char *name = luaL_checkstring(L, 3); - // Do it - ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, ""); - int objectid = env->addActiveObject(obj); - // If failed to add, return nothing (reads as nil) - if(objectid == 0) - return 0; - // Return ObjectRef - objectref_get_or_create(L, obj); - return 1; + 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 } - // EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil - // pos = {x=num, y=num, z=num} - static int l_add_item(lua_State *L) + static void Register(lua_State *L) { - //infostream<<"EnvRef::l_add_item()"<m_env; - if(env == NULL) return 0; - // pos - v3f pos = checkFloatPos(L, 2); - // item - ItemStack item = read_item(L, 3); - if(item.empty() || !item.isKnown(get_server(L)->idef())) - return 0; - // 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 - objectref_get_or_create(L, obj); - return 1; + 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 (PerlinNoise(seed, octaves, persistence) + lua_register(L, className, create_object); } +}; +const char LuaPerlinNoise::className[] = "PerlinNoise"; +const luaL_reg LuaPerlinNoise::methods[] = { + method(LuaPerlinNoise, get2d), + method(LuaPerlinNoise, get3d), + {0,0} +}; - // EnvRef:add_rat(pos) - // pos = {x=num, y=num, z=num} - static int l_add_rat(lua_State *L) - { - infostream<<"EnvRef::l_add_rat(): C++ mobs have been removed." - <<" Doing nothing."<m_env; if(env == NULL) return 0; - // Do it - v3s16 p = read_v3s16(L, 2); - NodeMetaRef::create(L, p, env); - 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; } - // EnvRef:get_player_by_name(name) - static int l_get_player_by_name(lua_State *L) + static int l_start(lua_State *L) { - EnvRef *o = checkobject(L, 1); + NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; - // Do it - const char *name = luaL_checkstring(L, 2); - ServerRemotePlayer *player = - static_cast(env->getPlayer(name)); - if(player == NULL){ - lua_pushnil(L); - return 1; - } - // Put player on stack - objectref_get_or_create(L, player); - return 1; + f32 t = luaL_checknumber(L,2); + env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0)); + return 0; } - // EnvRef:get_objects_inside_radius(pos, radius) - static int l_get_objects_inside_radius(lua_State *L) + static int l_stop(lua_State *L) { - // Get the table insert function - lua_getglobal(L, "table"); - lua_getfield(L, -1, "insert"); - int table_insert = lua_gettop(L); - // Get environemnt - EnvRef *o = checkobject(L, 1); + NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; - // Do it - v3f pos = checkFloatPos(L, 2); - float radius = luaL_checknumber(L, 3) * BS; - std::set ids = env->getObjectsInsideRadius(pos, radius); - lua_newtable(L); - int table = lua_gettop(L); - for(std::set::const_iterator - i = ids.begin(); i != ids.end(); i++){ - ServerActiveObject *obj = env->getActiveObject(*i); - // Insert object reference into table - lua_pushvalue(L, table_insert); - lua_pushvalue(L, table); - objectref_get_or_create(L, obj); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - } - return 1; + env->getMap().removeNodeTimer(o->m_p); + return 0; } - // EnvRef:set_timeofday(val) - // val = 0...1 - static int l_set_timeofday(lua_State *L) + static int l_is_started(lua_State *L) { - EnvRef *o = checkobject(L, 1); + NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; - // Do it - float timeofday_f = luaL_checknumber(L, 2); - assert(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 - // the server instead. - //env->setTimeOfDay(timeofday_mh); - get_server(L)->setTimeOfDay(timeofday_mh); - return 0; + + NodeTimer t = env->getMap().getNodeTimer(o->m_p); + lua_pushboolean(L,(t.timeout != 0)); + return 1; } - // EnvRef:get_timeofday() -> 0...1 - static int l_get_timeofday(lua_State *L) + static int l_get_timeout(lua_State *L) { - EnvRef *o = checkobject(L, 1); + NodeTimerRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; - // Do it - int timeofday_mh = env->getTimeOfDay(); - float timeofday_f = (float)timeofday_mh / 24000.0; - lua_pushnumber(L, timeofday_f); + + NodeTimer t = env->getMap().getNodeTimer(o->m_p); + lua_pushnumber(L,t.timeout); return 1; } - static int gc_object(lua_State *L) { - EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1)); - delete o; - return 0; + static int l_get_elapsed(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_pushnumber(L,t.elapsed); + return 1; } public: - EnvRef(ServerEnvironment *env): + NodeTimerRef(v3s16 p, ServerEnvironment *env): + m_p(p), m_env(env) { - //infostream<<"EnvRef created"< m_trigger_contents; - std::set m_required_neighbors; - float m_trigger_interval; - u32 m_trigger_chance; -public: - LuaABM(lua_State *L, int id, - const std::set &trigger_contents, - const std::set &required_neighbors, - float trigger_interval, u32 trigger_chance): - m_lua(L), - m_id(id), - m_trigger_contents(trigger_contents), - m_required_neighbors(required_neighbors), - m_trigger_interval(trigger_interval), - m_trigger_chance(trigger_chance) - { + 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; } - virtual std::set getTriggerContents() + + static EnvRef *checkobject(lua_State *L, int narg) { - return m_trigger_contents; + 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 } - virtual std::set getRequiredNeighbors() + + // Exported functions + + // EnvRef:set_node(pos, node) + // pos = {x=num, y=num, z=num} + static int l_set_node(lua_State *L) { - return m_required_neighbors; + 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; } - virtual float getTriggerInterval() + + static int l_add_node(lua_State *L) { - return m_trigger_interval; + return l_set_node(L); } - virtual u32 getTriggerChance() + + // EnvRef:remove_node(pos) + // pos = {x=num, y=num, z=num} + static int l_remove_node(lua_State *L) { - return m_trigger_chance; + 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; } - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, - 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); - - // Get minetest.registered_abms - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_abms"); - luaL_checktype(L, -1, LUA_TTABLE); - int registered_abms = lua_gettop(L); - // Get minetest.registered_abms[m_id] - lua_pushnumber(L, m_id); - 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"); - luaL_checktype(L, -1, LUA_TFUNCTION); - push_v3s16(L, p); + // 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()); - lua_pushnumber(L, active_object_count); - lua_pushnumber(L, active_object_count_wider); - if(lua_pcall(L, 4, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); + return 1; } -}; - -// debug(text) -// Writes a line to dstream -static int l_debug(lua_State *L) -{ - std::string text = lua_tostring(L, 1); - dstream << text << std::endl; - return 0; -} -// log([level,] text) -// Writes a line to the logger. -// The one-argument version logs to infostream. -// The two-argument version accept a log level: error, action, info, or verbose. -static int l_log(lua_State *L) -{ - std::string text; - LogMessageLevel level = LMT_INFO; - if(lua_isnone(L, 2)) + // EnvRef:get_node_or_nil(pos) + // pos = {x=num, y=num, z=num} + static int l_get_node_or_nil(lua_State *L) { - text = lua_tostring(L, 1); + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // pos + v3s16 pos = read_v3s16(L, 2); + // Do it + try{ + MapNode n = env->getMap().getNode(pos); + // Return node + pushnode(L, n, env->getGameDef()->ndef()); + return 1; + } catch(InvalidPositionException &e) + { + lua_pushnil(L); + return 1; + } } - else + + // EnvRef:get_node_light(pos, timeofday) + // pos = {x=num, y=num, z=num} + // timeofday: nil = current time, 0 = night, 0.5 = day + static int l_get_node_light(lua_State *L) { - std::string levelname = lua_tostring(L, 1); - text = lua_tostring(L, 2); - if(levelname == "error") - level = LMT_ERROR; - else if(levelname == "action") - level = LMT_ACTION; - else if(levelname == "verbose") - level = LMT_VERBOSE; + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // Do it + v3s16 pos = read_v3s16(L, 2); + u32 time_of_day = env->getTimeOfDay(); + 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, true); + MapNode n = env->getMap().getNodeNoEx(pos); + try{ + MapNode n = env->getMap().getNode(pos); + INodeDefManager *ndef = env->getGameDef()->ndef(); + lua_pushinteger(L, n.getLightBlend(dnr, ndef)); + return 1; + } catch(InvalidPositionException &e) + { + lua_pushnil(L); + return 1; + } } - log_printline(level, text); - 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 - IWritableItemDefManager *idef = - get_server(L)->getWritableItemDefManager(); - IWritableNodeDefManager *ndef = - get_server(L)->getWritableNodeDefManager(); + // 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()); - // Check if name is defined - lua_getfield(L, table, "name"); - if(lua_isstring(L, -1)){ - std::string name = lua_tostring(L, -1); - verbosestream<<"register_item_raw: "<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; } - // Check if on_use is defined + // 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); - // Read the item definition and register it - ItemDefinition def = read_item_definition(L, table); - idef->registerItem(def); + // 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; + } - // Read the node definition (content features) and register it - if(def.type == ITEM_NODE) + // EnvRef:punch_node(pos) + // pos = {x=num, y=num, z=num} + static int l_punch_node(lua_State *L) { - ContentFeatures f = read_content_features(L, table); - ndef->set(f.name, f); - } + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + v3s16 pos = read_v3s16(L, 2); - return 0; /* number of results */ -} + // 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; + } -// register_alias_raw(name, convert_to_name) -static int l_register_alias_raw(lua_State *L) -{ - std::string name = luaL_checkstring(L, 1); - std::string convert_to = luaL_checkstring(L, 2); + // 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; + } - // 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 */ -} + // 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; + } -// helper for register_craft -static bool read_craft_recipe_shaped(lua_State *L, int index, - int &width, std::vector &recipe) -{ - if(index < 0) + // EnvRef:add_entity(pos, entityname) -> ObjectRef or nil + // pos = {x=num, y=num, z=num} + static int l_add_entity(lua_State *L) + { + //infostream<<"EnvRef::l_add_entity()"<m_env; + if(env == NULL) return 0; + // pos + v3f pos = checkFloatPos(L, 2); + // content + const char *name = luaL_checkstring(L, 3); + // Do it + ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, ""); + int objectid = env->addActiveObject(obj); + // If failed to add, return nothing (reads as nil) + if(objectid == 0) + return 0; + // Return ObjectRef + objectref_get_or_create(L, obj); + return 1; + } + + // EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil + // pos = {x=num, y=num, z=num} + static int l_add_item(lua_State *L) + { + //infostream<<"EnvRef::l_add_item()"<m_env; + if(env == NULL) return 0; + // pos + v3f pos = checkFloatPos(L, 2); + // item + ItemStack item = read_item(L, 3); + if(item.empty() || !item.isKnown(get_server(L)->idef())) + return 0; + // Use minetest.spawn_item to spawn a __builtin:item + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "spawn_item"); + if(lua_isnil(L, -1)) + return 0; + lua_pushvalue(L, 2); + lua_pushstring(L, item.getItemString().c_str()); + if(lua_pcall(L, 2, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + 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 + objectref_get_or_create(L, obj); + return 1;*/ + } + + // EnvRef:add_rat(pos) + // pos = {x=num, y=num, z=num} + static int l_add_rat(lua_State *L) + { + infostream<<"EnvRef::l_add_rat(): C++ mobs have been removed." + <<" Doing nothing."<m_env; + if(env == NULL) return 0; + // Do it + const char *name = luaL_checkstring(L, 2); + Player *player = env->getPlayer(name); + if(player == NULL){ + lua_pushnil(L); + return 1; + } + PlayerSAO *sao = player->getPlayerSAO(); + if(sao == NULL){ + lua_pushnil(L); + return 1; + } + // Put player on stack + objectref_get_or_create(L, sao); + return 1; + } + + // EnvRef:get_objects_inside_radius(pos, radius) + static int l_get_objects_inside_radius(lua_State *L) + { + // Get the table insert function + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + // Get environemnt + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // Do it + v3f pos = checkFloatPos(L, 2); + float radius = luaL_checknumber(L, 3) * BS; + std::set ids = env->getObjectsInsideRadius(pos, radius); + lua_newtable(L); + int table = lua_gettop(L); + for(std::set::const_iterator + i = ids.begin(); i != ids.end(); i++){ + ServerActiveObject *obj = env->getActiveObject(*i); + // Insert object reference into table + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + objectref_get_or_create(L, obj); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } + return 1; + } + + // EnvRef:set_timeofday(val) + // val = 0...1 + static int l_set_timeofday(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // Do it + float timeofday_f = luaL_checknumber(L, 2); + assert(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 + // the server instead. + //env->setTimeOfDay(timeofday_mh); + get_server(L)->setTimeOfDay(timeofday_mh); + return 0; + } + + // EnvRef:get_timeofday() -> 0...1 + static int l_get_timeofday(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // Do it + int timeofday_mh = env->getTimeOfDay(); + float timeofday_f = (float)timeofday_mh / 24000.0; + lua_pushnumber(L, timeofday_f); + return 1; + } + + + // EnvRef:find_node_near(pos, radius, nodenames) -> pos or nil + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_node_near(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + INodeDefManager *ndef = get_server(L)->ndef(); + v3s16 pos = read_v3s16(L, 2); + int radius = luaL_checkinteger(L, 3); + std::set filter; + if(lua_istable(L, 4)){ + int table = 4; + 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, 4)){ + ndef->getIds(lua_tostring(L, 4), filter); + } + + for(int d=1; d<=radius; d++){ + core::list list; + getFacePositions(list, d); + for(core::list::Iterator i = list.begin(); + i != list.end(); i++){ + v3s16 p = pos + (*i); + content_t c = env->getMap().getNodeNoEx(p).getContent(); + if(filter.count(c) != 0){ + push_v3s16(L, p); + return 1; + } + } + } + return 0; + } + + // EnvRef:find_nodes_in_area(minp, maxp, nodenames) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_in_area(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + INodeDefManager *ndef = get_server(L)->ndef(); + v3s16 minp = read_v3s16(L, 2); + v3s16 maxp = read_v3s16(L, 3); + std::set filter; + if(lua_istable(L, 4)){ + int table = 4; + 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, 4)){ + ndef->getIds(lua_tostring(L, 4), filter); + } + + // Get the table insert function + 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++) + 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){ + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + push_v3s16(L, p); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } + } + return 1; + } + + // EnvRef:get_perlin(seeddiff, octaves, persistence, scale) + // returns world-specific PerlinNoise + static int l_get_perlin(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + + int seeddiff = luaL_checkint(L, 2); + int octaves = luaL_checkint(L, 3); + 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; + luaL_getmetatable(L, "PerlinNoise"); + lua_setmetatable(L, -2); + 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) + { + //infostream<<"EnvRef created"< get next value + static int l_next(lua_State *L) + { + 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)) + 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; + int val = pseudo.next(); + val = (val % (max-min+1)) + min; + lua_pushinteger(L, val); + return 1; + } + +public: + LuaPseudoRandom(int seed): + m_pseudo(seed) + { + } + + ~LuaPseudoRandom() + { + } + + const PseudoRandom& getItem() const + { + return m_pseudo; + } + PseudoRandom& getItem() + { + return m_pseudo; + } + + // LuaPseudoRandom(seed) + // Creates an LuaPseudoRandom and leaves it on top of stack + static int 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; + } + + static LuaPseudoRandom* 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 + } + + 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 + + // Can be created from Lua (LuaPseudoRandom(seed)) + lua_register(L, className, create_object); + } +}; +const char LuaPseudoRandom::className[] = "PseudoRandom"; +const luaL_reg LuaPseudoRandom::methods[] = { + method(LuaPseudoRandom, next), + {0,0} +}; + + + +/* + LuaABM +*/ + +class LuaABM : public ActiveBlockModifier +{ +private: + lua_State *m_lua; + int m_id; + + std::set m_trigger_contents; + std::set m_required_neighbors; + float m_trigger_interval; + u32 m_trigger_chance; +public: + LuaABM(lua_State *L, int id, + const std::set &trigger_contents, + const std::set &required_neighbors, + float trigger_interval, u32 trigger_chance): + m_lua(L), + m_id(id), + m_trigger_contents(trigger_contents), + m_required_neighbors(required_neighbors), + m_trigger_interval(trigger_interval), + m_trigger_chance(trigger_chance) + { + } + virtual std::set getTriggerContents() + { + return m_trigger_contents; + } + virtual std::set getRequiredNeighbors() + { + return m_required_neighbors; + } + virtual float getTriggerInterval() + { + return m_trigger_interval; + } + virtual u32 getTriggerChance() + { + return m_trigger_chance; + } + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, + 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); + + // Get minetest.registered_abms + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_abms"); + luaL_checktype(L, -1, LUA_TTABLE); + int registered_abms = lua_gettop(L); + + // Get minetest.registered_abms[m_id] + lua_pushnumber(L, m_id); + 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"); + luaL_checktype(L, -1, LUA_TFUNCTION); + push_v3s16(L, p); + 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, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } +}; + +/* + ServerSoundParams +*/ + +static void read_server_sound_params(lua_State *L, int index, + ServerSoundParams ¶ms) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + // Clear + params = ServerSoundParams(); + if(lua_istable(L, index)){ + getfloatfield(L, index, "gain", params.gain); + getstringfield(L, index, "to_player", params.to_player); + lua_getfield(L, index, "pos"); + if(!lua_isnil(L, -1)){ + v3f p = read_v3f(L, -1)*BS; + params.pos = p; + params.type = ServerSoundParams::SSP_POSITIONAL; + } + lua_pop(L, 1); + lua_getfield(L, index, "object"); + if(!lua_isnil(L, -1)){ + ObjectRef *ref = ObjectRef::checkobject(L, -1); + ServerActiveObject *sao = ObjectRef::getobject(ref); + if(sao){ + params.object = sao->getId(); + params.type = ServerSoundParams::SSP_OBJECT; + } + } + lua_pop(L, 1); + params.max_hear_distance = BS*getfloatfield_default(L, index, + "max_hear_distance", params.max_hear_distance/BS); + getboolfield(L, index, "loop", params.loop); + } +} + +/* + Global functions +*/ + +// debug(text) +// Writes a line to dstream +static int l_debug(lua_State *L) +{ + std::string text = lua_tostring(L, 1); + dstream << text << std::endl; + return 0; +} + +// log([level,] text) +// Writes a line to the logger. +// The one-argument version logs to infostream. +// The two-argument version accept a log level: error, action, info, or verbose. +static int l_log(lua_State *L) +{ + std::string text; + LogMessageLevel level = LMT_INFO; + if(lua_isnone(L, 2)) + { + text = lua_tostring(L, 1); + } + else + { + std::string levelname = lua_tostring(L, 1); + text = lua_tostring(L, 2); + if(levelname == "error") + level = LMT_ERROR; + else if(levelname == "action") + level = LMT_ACTION; + else if(levelname == "verbose") + level = LMT_VERBOSE; + } + log_printline(level, text); + return 0; +} + +// request_shutdown() +static int l_request_shutdown(lua_State *L) +{ + 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 + IWritableItemDefManager *idef = + get_server(L)->getWritableItemDefManager(); + IWritableNodeDefManager *ndef = + get_server(L)->getWritableNodeDefManager(); + + // Check if name is defined + std::string name; + lua_getfield(L, table, "name"); + if(lua_isstring(L, -1)){ + name = lua_tostring(L, -1); + verbosestream<<"register_item_raw: "<registerItem(def); + + // Read the node definition (content features) and register it + if(def.type == ITEM_NODE) + { + ContentFeatures f = read_content_features(L, table); + ndef->set(f.name, f); + } + + return 0; /* number of results */ +} + +// register_alias_raw(name, convert_to_name) +static int l_register_alias_raw(lua_State *L) +{ + std::string name = luaL_checkstring(L, 1); + std::string convert_to = luaL_checkstring(L, 2); + + // 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 */ +} + +// helper for register_craft +static bool read_craft_recipe_shaped(lua_State *L, int index, + int &width, std::vector &recipe) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + int rowcount = 0; + while(lua_next(L, index) != 0){ + int colcount = 0; + // key at index -2 and value at index -1 + if(!lua_istable(L, -1)) + return false; + int table2 = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table2) != 0){ + // key at index -2 and value at index -1 + if(!lua_isstring(L, -1)) + return false; + recipe.push_back(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + colcount++; + } + if(rowcount == 0){ + width = colcount; + } else { + if(colcount != width) + return false; + } + // removes value, keeps key for next iteration + lua_pop(L, 1); + rowcount++; + } + return width != 0; +} + +// helper for register_craft +static bool read_craft_recipe_shapeless(lua_State *L, int index, + std::vector &recipe) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + if(!lua_isstring(L, -1)) + return false; + recipe.push_back(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return true; +} + +// helper for register_craft +static bool read_craft_replacements(lua_State *L, int index, + CraftReplacements &replacements) +{ + if(index < 0) index = lua_gettop(L) + 1 + index; - if(!lua_istable(L, index)) - return false; + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + if(!lua_istable(L, -1)) + return false; + lua_rawgeti(L, -1, 1); + if(!lua_isstring(L, -1)) + return false; + std::string replace_from = lua_tostring(L, -1); + lua_pop(L, 1); + lua_rawgeti(L, -1, 2); + if(!lua_isstring(L, -1)) + return false; + std::string replace_to = lua_tostring(L, -1); + lua_pop(L, 1); + replacements.pairs.push_back( + std::make_pair(replace_from, replace_to)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return true; +} +// register_craft({output=item, recipe={{item00,item10},{item01,item11}}) +static int l_register_craft(lua_State *L) +{ + //infostream<<"register_craft"<getWritableCraftDefManager(); + + std::string type = getstringfield_default(L, table, "type", "shaped"); + + /* + CraftDefinitionShaped + */ + if(type == "shaped"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition is missing an output"); + + int width = 0; + std::vector recipe; + lua_getfield(L, table, "recipe"); + if(lua_isnil(L, -1)) + throw LuaError(L, "Crafting definition is missing a recipe" + " (output=\"" + output + "\")"); + if(!read_craft_recipe_shaped(L, -1, width, recipe)) + throw LuaError(L, "Invalid crafting recipe" + " (output=\"" + output + "\")"); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionShaped( + output, width, recipe, replacements); + craftdef->registerCraft(def); + } + /* + CraftDefinitionShapeless + */ + else if(type == "shapeless"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition (shapeless)" + " is missing an output"); + + std::vector recipe; + lua_getfield(L, table, "recipe"); + if(lua_isnil(L, -1)) + throw LuaError(L, "Crafting definition (shapeless)" + " is missing a recipe" + " (output=\"" + output + "\")"); + if(!read_craft_recipe_shapeless(L, -1, recipe)) + throw LuaError(L, "Invalid crafting recipe" + " (output=\"" + output + "\")"); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionShapeless( + output, recipe, replacements); + craftdef->registerCraft(def); + } + /* + CraftDefinitionToolRepair + */ + else if(type == "toolrepair"){ + float additional_wear = getfloatfield_default(L, table, + "additional_wear", 0.0); + + CraftDefinition *def = new CraftDefinitionToolRepair( + additional_wear); + craftdef->registerCraft(def); + } + /* + CraftDefinitionCooking + */ + else if(type == "cooking"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition (cooking)" + " is missing an output"); + + std::string recipe = getstringfield_default(L, table, "recipe", ""); + if(recipe == "") + throw LuaError(L, "Crafting definition (cooking)" + " is missing a recipe" + " (output=\"" + output + "\")"); + + 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, replacements); + craftdef->registerCraft(def); + } + /* + CraftDefinitionFuel + */ + else if(type == "fuel"){ + std::string recipe = getstringfield_default(L, table, "recipe", ""); + if(recipe == "") + throw LuaError(L, "Crafting definition (fuel)" + " is missing a recipe"); + + 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, replacements); + craftdef->registerCraft(def); + } + else + { + throw LuaError(L, "Unknown crafting definition type: \"" + type + "\""); + } + + lua_pop(L, 1); + return 0; /* number of results */ +} + +// setting_set(name, value) +static int l_setting_set(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + const char *value = luaL_checkstring(L, 2); + g_settings->set(name, value); + return 0; +} + +// setting_get(name) +static int l_setting_get(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + try{ + std::string value = g_settings->get(name); + lua_pushstring(L, value.c_str()); + } catch(SettingNotFoundException &e){ + lua_pushnil(L); + } + return 1; +} + +// setting_getbool(name) +static int l_setting_getbool(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + try{ + bool value = g_settings->getBool(name); + lua_pushboolean(L, value); + } catch(SettingNotFoundException &e){ + lua_pushnil(L); + } + return 1; +} + +// chat_send_all(text) +static int l_chat_send_all(lua_State *L) +{ + const char *text = luaL_checkstring(L, 1); + // Get server from registry + Server *server = get_server(L); + // Send + server->notifyPlayers(narrow_to_wide(text)); + return 0; +} + +// chat_send_player(name, text) +static int l_chat_send_player(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + const char *text = luaL_checkstring(L, 2); + // Get server from registry + Server *server = get_server(L); + // Send + server->notifyPlayer(name, narrow_to_wide(text)); + return 0; +} + +// get_player_privs(name, text) +static int l_get_player_privs(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + // Get server from registry + Server *server = get_server(L); + // Do it + lua_newtable(L); + int table = lua_gettop(L); + std::set privs_s = server->getPlayerEffectivePrivs(name); + for(std::set::const_iterator + i = privs_s.begin(); i != privs_s.end(); i++){ + lua_pushboolean(L, true); + lua_setfield(L, table, i->c_str()); + } + lua_pushvalue(L, table); + 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) +{ + InventoryLocation loc; + + std::string type = checkstringfield(L, 1, "type"); + if(type == "player"){ + std::string name = checkstringfield(L, 1, "name"); + loc.setPlayer(name); + } else if(type == "node"){ + 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 + lua_pushnil(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) +{ + std::map groups; + read_groups(L, 1, groups); + ToolCapabilities tp = read_tool_capabilities(L, 2); + if(lua_isnoneornil(L, 3)) + push_dig_params(L, getDigParams(groups, &tp)); + else + push_dig_params(L, getDigParams(groups, &tp, + luaL_checknumber(L, 3))); + return 1; +} + +// get_hit_params(groups, tool_capabilities[, time_from_last_punch]) +static int l_get_hit_params(lua_State *L) +{ + std::map groups; + read_groups(L, 1, groups); + ToolCapabilities tp = read_tool_capabilities(L, 2); + if(lua_isnoneornil(L, 3)) + push_hit_params(L, getHitParams(groups, &tp)); + else + push_hit_params(L, getHitParams(groups, &tp, + luaL_checknumber(L, 3))); + return 1; +} + +// get_current_modname() +static int l_get_current_modname(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); + return 1; +} + +// get_modpath(modname) +static int l_get_modpath(lua_State *L) +{ + std::string modname = luaL_checkstring(L, 1); + // Do it + if(modname == "__builtin"){ + std::string path = get_server(L)->getBuiltinLuaPath(); + lua_pushstring(L, path.c_str()); + return 1; + } + const ModSpec *mod = get_server(L)->getModSpec(modname); + if(!mod){ + lua_pushnil(L); + return 1; + } + lua_pushstring(L, mod->path.c_str()); + 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) +{ + std::string worldpath = get_server(L)->getWorldPath(); + lua_pushstring(L, worldpath.c_str()); + return 1; +} + +// sound_play(spec, parameters) +static int l_sound_play(lua_State *L) +{ + SimpleSoundSpec spec; + read_soundspec(L, 1, spec); + ServerSoundParams params; + read_server_sound_params(L, 2, params); + s32 handle = get_server(L)->playSound(spec, params); + lua_pushinteger(L, handle); + return 1; +} + +// sound_stop(handle) +static int l_sound_stop(lua_State *L) +{ + int handle = luaL_checkinteger(L, 1); + get_server(L)->stopSound(handle); + return 0; +} + +// is_singleplayer() +static int l_is_singleplayer(lua_State *L) +{ + lua_pushboolean(L, get_server(L)->isSingleplayer()); + return 1; +} + +// get_password_hash(name, raw_password) +static int l_get_password_hash(lua_State *L) +{ + 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)); + lua_pushstring(L, hash.c_str()); + return 1; +} + +// notify_authentication_modified(name) +static int l_notify_authentication_modified(lua_State *L) +{ + std::string name = ""; + if(lua_isstring(L, 1)) + name = lua_tostring(L, 1); + get_server(L)->reportPrivsModified(name); + 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} +}; + +/* + Main export function +*/ + +void scriptapi_export(lua_State *L, Server *server) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + verbosestream<<"scriptapi_export()"< trigger_contents; + lua_getfield(L, current_abm, "nodenames"); + 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 + luaL_checktype(L, -1, LUA_TSTRING); + trigger_contents.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if(lua_isstring(L, -1)){ + trigger_contents.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + std::set required_neighbors; + lua_getfield(L, current_abm, "neighbors"); + 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 + luaL_checktype(L, -1, LUA_TSTRING); + required_neighbors.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if(lua_isstring(L, -1)){ + required_neighbors.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + float trigger_interval = 10.0; + getfloatfield(L, current_abm, "interval", trigger_interval); + + int trigger_chance = 50; + getintfield(L, current_abm, "chance", trigger_chance); + + LuaABM *abm = new LuaABM(L, id, trigger_contents, + required_neighbors, trigger_interval, trigger_chance); + + env->addActiveBlockModifier(abm); + + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); +} + +#if 0 +// Dump stack top with the dump2 function +static void dump2(lua_State *L, const char *name) +{ + // Dump object (debug) + lua_getglobal(L, "dump2"); + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_pushvalue(L, -2); // Get previous stack top as first parameter + lua_pushstring(L, name); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} +#endif + +/* + object_reference +*/ + +void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_add_object_reference: id="<getId()<getId()); // Push id + lua_pushvalue(L, object); // Copy object to top of stack + lua_settable(L, objectstable); +} + +void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_rm_object_reference: id="<getId()<getId()); // Push id + lua_gettable(L, objectstable); + // Set object reference to NULL + ObjectRef::set_null(L); + lua_pop(L, 1); // pop object + + // Set object_refs[id] = nil + lua_pushnumber(L, cobj->getId()); // Push id + lua_pushnil(L); + lua_settable(L, objectstable); +} + +/* + misc +*/ + +// What scriptapi_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. +// Modes only affect the case where 0 or >= 2 callbacks are defined. +enum RunCallbacksMode +{ + // Returns the return value of the first callback + // Returns nil if list of callbacks is empty + RUN_CALLBACKS_MODE_FIRST, + // Returns the return value of the last callback + // Returns nil if list of callbacks is empty + RUN_CALLBACKS_MODE_LAST, + // If any callback returns a false value, the first such is returned + // Otherwise, the first callback's return value (trueish) is returned + // Returns true if list of callbacks is empty + RUN_CALLBACKS_MODE_AND, + // Like above, but stops calling callbacks (short circuit) + // after seeing the first false value + RUN_CALLBACKS_MODE_AND_SC, + // If any callback returns a true value, the first such is returned + // Otherwise, the first callback's return value (falseish) is returned + // Returns false if list of callbacks is empty + RUN_CALLBACKS_MODE_OR, + // Like above, but stops calling callbacks (short circuit) + // after seeing the first true value + RUN_CALLBACKS_MODE_OR_SC, + // Note: "a true value" and "a false value" refer to values that + // are converted by lua_toboolean to true or false, respectively. +}; + +// Push the list of callbacks (a lua table). +// Then push nargs arguments. +// Then call this function, which +// - runs the callbacks +// - removes the table and arguments from the lua stack +// - pushes the return value, computed depending on mode +static void scriptapi_run_callbacks(lua_State *L, int nargs, + RunCallbacksMode mode) +{ + // Insert the return value into the lua stack, below the table + assert(lua_gettop(L) >= nargs + 1); + lua_pushnil(L); + lua_insert(L, -(nargs + 1) - 1); + // Stack now looks like this: + // ... ... + + int rv = lua_gettop(L) - nargs - 1; + int table = rv + 1; + int arg = table + 1; + + luaL_checktype(L, table, LUA_TTABLE); + + // Foreach + lua_pushnil(L); + bool first_loop = true; + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TFUNCTION); + // Call function + for(int i = 0; i < nargs; i++) + lua_pushvalue(L, arg+i); + if(lua_pcall(L, nargs, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + + // Move return value to designated space in stack + // Or pop it + if(first_loop){ + // Result of first callback is always moved + lua_replace(L, rv); + first_loop = false; + } else { + // Otherwise, what happens depends on the mode + if(mode == RUN_CALLBACKS_MODE_FIRST) + lua_pop(L, 1); + else if(mode == RUN_CALLBACKS_MODE_LAST) + lua_replace(L, rv); + else if(mode == RUN_CALLBACKS_MODE_AND || + mode == RUN_CALLBACKS_MODE_AND_SC){ + 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((bool)lua_toboolean(L, rv) == false && + (bool)lua_toboolean(L, -1) == true) + lua_replace(L, rv); + else + lua_pop(L, 1); + } + else + assert(0); + } + + // Handle short circuit modes + if(mode == RUN_CALLBACKS_MODE_AND_SC && + (bool)lua_toboolean(L, rv) == false) + break; + else if(mode == RUN_CALLBACKS_MODE_OR_SC && + (bool)lua_toboolean(L, rv) == true) + break; + + // value removed, keep key for next iteration + } + + // Remove stuff from stack, leaving only the return value + lua_settop(L, rv); - lua_pushnil(L); - int rowcount = 0; - while(lua_next(L, index) != 0){ - int colcount = 0; - // key at index -2 and value at index -1 - if(!lua_istable(L, -1)) - return false; - int table2 = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table2) != 0){ - // key at index -2 and value at index -1 - if(!lua_isstring(L, -1)) - return false; - recipe.push_back(lua_tostring(L, -1)); - // removes value, keeps key for next iteration + // Fix return value in case no callbacks were called + if(first_loop){ + if(mode == RUN_CALLBACKS_MODE_AND || + mode == RUN_CALLBACKS_MODE_AND_SC){ lua_pop(L, 1); - colcount++; + lua_pushboolean(L, true); } - if(rowcount == 0){ - width = colcount; - } else { - if(colcount != width) - return false; + else if(mode == RUN_CALLBACKS_MODE_OR || + mode == RUN_CALLBACKS_MODE_OR_SC){ + lua_pop(L, 1); + lua_pushboolean(L, false); } - // removes value, keeps key for next iteration - lua_pop(L, 1); - rowcount++; } - return width != 0; } -// helper for register_craft -static bool read_craft_recipe_shapeless(lua_State *L, int index, - std::vector &recipe) +bool scriptapi_on_chat_message(lua_State *L, const std::string &name, + const std::string &message) { - if(index < 0) - index = lua_gettop(L) + 1 + index; - - if(!lua_istable(L, index)) - return false; + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - lua_pushnil(L); - while(lua_next(L, index) != 0){ - // key at index -2 and value at index -1 - if(!lua_isstring(L, -1)) - return false; - recipe.push_back(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - return true; + // Get minetest.registered_on_chat_messages + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_chat_messages"); + // Call callbacks + lua_pushstring(L, name.c_str()); + lua_pushstring(L, message.c_str()); + scriptapi_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR_SC); + bool ate = lua_toboolean(L, -1); + return ate; } -// helper for register_craft -static bool read_craft_replacements(lua_State *L, int index, - CraftReplacements &replacements) +void scriptapi_on_shutdown(lua_State *L) { - if(index < 0) - index = lua_gettop(L) + 1 + index; - - if(!lua_istable(L, index)) - return false; + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - lua_pushnil(L); - while(lua_next(L, index) != 0){ - // key at index -2 and value at index -1 - if(!lua_istable(L, -1)) - return false; - lua_rawgeti(L, -1, 1); - if(!lua_isstring(L, -1)) - return false; - std::string replace_from = lua_tostring(L, -1); - lua_pop(L, 1); - lua_rawgeti(L, -1, 2); - if(!lua_isstring(L, -1)) - return false; - std::string replace_to = lua_tostring(L, -1); - lua_pop(L, 1); - replacements.pairs.push_back( - std::make_pair(replace_from, replace_to)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - return true; + // 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); } -// register_craft({output=item, recipe={{item00,item10},{item01,item11}}) -static int l_register_craft(lua_State *L) -{ - //infostream<<"register_craft"<getWritableCraftDefManager(); - - std::string type = getstringfield_default(L, table, "type", "shaped"); - /* - CraftDefinitionShaped - */ - if(type == "shaped"){ - std::string output = getstringfield_default(L, table, "output", ""); - if(output == "") - throw LuaError(L, "Crafting definition is missing an output"); +void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - int width = 0; - std::vector recipe; - lua_getfield(L, table, "recipe"); - if(lua_isnil(L, -1)) - throw LuaError(L, "Crafting definition is missing a recipe" - " (output=\"" + output + "\")"); - if(!read_craft_recipe_shaped(L, -1, width, recipe)) - throw LuaError(L, "Invalid crafting recipe" - " (output=\"" + output + "\")"); + // Get minetest.registered_on_newplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_newplayers"); + // Call callbacks + objectref_get_or_create(L, player); + scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); +} - CraftReplacements replacements; - lua_getfield(L, table, "replacements"); - if(!lua_isnil(L, -1)) - { - if(!read_craft_replacements(L, -1, replacements)) - throw LuaError(L, "Invalid replacements" - " (output=\"" + output + "\")"); - } +void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - CraftDefinition *def = new CraftDefinitionShaped( - output, width, recipe, replacements); - craftdef->registerCraft(def); - } - /* - CraftDefinitionShapeless - */ - else if(type == "shapeless"){ - std::string output = getstringfield_default(L, table, "output", ""); - if(output == "") - throw LuaError(L, "Crafting definition (shapeless)" - " is missing an output"); + // Get minetest.registered_on_dieplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_dieplayers"); + // Call callbacks + objectref_get_or_create(L, player); + scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); +} - std::vector recipe; - lua_getfield(L, table, "recipe"); - if(lua_isnil(L, -1)) - throw LuaError(L, "Crafting definition (shapeless)" - " is missing a recipe" - " (output=\"" + output + "\")"); - if(!read_craft_recipe_shapeless(L, -1, recipe)) - throw LuaError(L, "Invalid crafting recipe" - " (output=\"" + output + "\")"); +bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - CraftReplacements replacements; - lua_getfield(L, table, "replacements"); - if(!lua_isnil(L, -1)) - { - if(!read_craft_replacements(L, -1, replacements)) - throw LuaError(L, "Invalid replacements" - " (output=\"" + output + "\")"); - } + // Get minetest.registered_on_respawnplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_respawnplayers"); + // Call callbacks + objectref_get_or_create(L, player); + scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_OR); + bool positioning_handled_by_some = lua_toboolean(L, -1); + return positioning_handled_by_some; +} - CraftDefinition *def = new CraftDefinitionShapeless( - output, recipe, replacements); - craftdef->registerCraft(def); - } - /* - CraftDefinitionToolRepair - */ - else if(type == "toolrepair"){ - float additional_wear = getfloatfield_default(L, table, - "additional_wear", 0.0); +void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - CraftDefinition *def = new CraftDefinitionToolRepair( - additional_wear); - craftdef->registerCraft(def); - } - /* - CraftDefinitionCooking - */ - else if(type == "cooking"){ - std::string output = getstringfield_default(L, table, "output", ""); - if(output == "") - throw LuaError(L, "Crafting definition (cooking)" - " is missing an output"); + // Get minetest.registered_on_joinplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_joinplayers"); + // Call callbacks + objectref_get_or_create(L, player); + scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); +} - std::string recipe = getstringfield_default(L, table, "recipe", ""); - if(recipe == "") - throw LuaError(L, "Crafting definition (cooking)" - " is missing a recipe" - " (output=\"" + output + "\")"); +void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - float cooktime = getfloatfield_default(L, table, "cooktime", 3.0); + // Get minetest.registered_on_leaveplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_leaveplayers"); + // Call callbacks + objectref_get_or_create(L, player); + scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); +} - CraftDefinition *def = new CraftDefinitionCooking( - output, recipe, cooktime); - craftdef->registerCraft(def); +static void get_auth_handler(lua_State *L) +{ + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_auth_handler"); + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + lua_getfield(L, -1, "builtin_auth_handler"); } - /* - CraftDefinitionFuel - */ - else if(type == "fuel"){ - std::string recipe = getstringfield_default(L, table, "recipe", ""); - if(recipe == "") - throw LuaError(L, "Crafting definition (fuel)" - " is missing a recipe"); + if(lua_type(L, -1) != LUA_TTABLE) + throw LuaError(L, "Authentication handler table not valid"); +} - float burntime = getfloatfield_default(L, table, "burntime", 1.0); +bool scriptapi_get_auth(lua_State *L, const std::string &playername, + std::string *dst_password, std::set *dst_privs) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - CraftDefinition *def = new CraftDefinitionFuel( - recipe, burntime); - craftdef->registerCraft(def); - } - else - { - throw LuaError(L, "Unknown crafting definition type: \"" + type + "\""); - } + get_auth_handler(L); + lua_getfield(L, -1, "get_auth"); + if(lua_type(L, -1) != LUA_TFUNCTION) + throw LuaError(L, "Authentication handler missing get_auth"); + 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) + throw LuaError(L, "Authentication handler didn't return password"); + if(dst_password) + *dst_password = password; + + lua_getfield(L, -1, "privileges"); + if(!lua_istable(L, -1)) + throw LuaError(L, + "Authentication handler didn't return privilege table"); + if(dst_privs) + read_privileges(L, -1, *dst_privs); lua_pop(L, 1); - return 0; /* number of results */ + + return true; } -// setting_get(name) -static int l_setting_get(lua_State *L) +void scriptapi_create_auth(lua_State *L, const std::string &playername, + const std::string &password) { - const char *name = luaL_checkstring(L, 1); - try{ - std::string value = g_settings->get(name); - lua_pushstring(L, value.c_str()); - } catch(SettingNotFoundException &e){ - lua_pushnil(L); - } - return 1; -} + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -// setting_getbool(name) -static int l_setting_getbool(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - try{ - bool value = g_settings->getBool(name); - lua_pushboolean(L, value); - } catch(SettingNotFoundException &e){ - lua_pushnil(L); - } - return 1; + get_auth_handler(L); + lua_getfield(L, -1, "create_auth"); + if(lua_type(L, -1) != LUA_TFUNCTION) + throw LuaError(L, "Authentication handler missing create_auth"); + lua_pushstring(L, playername.c_str()); + lua_pushstring(L, password.c_str()); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); } -// chat_send_all(text) -static int l_chat_send_all(lua_State *L) +bool scriptapi_set_password(lua_State *L, const std::string &playername, + const std::string &password) { - const char *text = luaL_checkstring(L, 1); - // Get server from registry - Server *server = get_server(L); - // Send - server->notifyPlayers(narrow_to_wide(text)); - return 0; -} + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -// chat_send_player(name, text) -static int l_chat_send_player(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - const char *text = luaL_checkstring(L, 2); - // Get server from registry - Server *server = get_server(L); - // Send - server->notifyPlayer(name, narrow_to_wide(text)); - return 0; + get_auth_handler(L); + lua_getfield(L, -1, "set_password"); + if(lua_type(L, -1) != LUA_TFUNCTION) + throw LuaError(L, "Authentication handler missing set_password"); + lua_pushstring(L, playername.c_str()); + lua_pushstring(L, password.c_str()); + if(lua_pcall(L, 2, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return lua_toboolean(L, -1); } -// get_player_privs(name, text) -static int l_get_player_privs(lua_State *L) +/* + player +*/ + +void scriptapi_on_player_receive_fields(lua_State *L, + ServerActiveObject *player, + const std::string &formname, + const std::map &fields) { - const char *name = luaL_checkstring(L, 1); - // Get server from registry - Server *server = get_server(L); - // Do it + 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); - int table = lua_gettop(L); - u64 privs_i = server->getPlayerEffectivePrivs(name); - std::set privs_s = privsToSet(privs_i); - for(std::set::const_iterator - i = privs_s.begin(); i != privs_s.end(); i++){ - lua_pushboolean(L, true); - lua_setfield(L, table, i->c_str()); + 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); } - lua_pushvalue(L, table); - return 1; + scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_OR_SC); } -// get_inventory(location) -static int l_get_inventory(lua_State *L) +/* + item callbacks and node callbacks +*/ + +// Retrieves minetest.registered_items[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 +// 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) { - InventoryLocation loc; + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_items"); + lua_remove(L, -2); + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, name); + lua_remove(L, -2); + // Should be a table + if(lua_type(L, -1) != LUA_TTABLE) + { + // Report error and clean up + errorstream<<"Item \""<getInventory(loc) != NULL) - InvRef::create(L, loc); else - lua_pushnil(L); - return 1; + { + errorstream<<"Item \""< groups; - read_groups(L, 1, groups); - ToolCapabilities tp = read_tool_capabilities(L, 2); - if(lua_isnoneornil(L, 3)) - push_dig_params(L, getDigParams(groups, &tp)); - else - push_dig_params(L, getDigParams(groups, &tp, - luaL_checknumber(L, 3))); - return 1; -} + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -// get_hit_params(groups, tool_capabilities[, time_from_last_punch]) -static int l_get_hit_params(lua_State *L) -{ - std::map groups; - read_groups(L, 1, groups); - ToolCapabilities tp = read_tool_capabilities(L, 2); - if(lua_isnoneornil(L, 3)) - push_hit_params(L, getHitParams(groups, &tp)); - else - push_hit_params(L, getHitParams(groups, &tp, - luaL_checknumber(L, 3))); - return 1; + // Push callback function on stack + if(!get_item_callback(L, item.name.c_str(), "on_drop")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectref_get_or_create(L, dropper); + pushFloatPos(L, pos); + if(lua_pcall(L, 3, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L, -1); + return true; } -// get_current_modname() -static int l_get_current_modname(lua_State *L) +bool scriptapi_item_on_place(lua_State *L, ItemStack &item, + ServerActiveObject *placer, const PointedThing &pointed) { - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); - return 1; + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_item_callback(L, item.name.c_str(), "on_place")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectref_get_or_create(L, placer); + push_pointed_thing(L, pointed); + if(lua_pcall(L, 3, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L, -1); + return true; } -// get_modpath(modname) -static int l_get_modpath(lua_State *L) +bool scriptapi_item_on_use(lua_State *L, ItemStack &item, + ServerActiveObject *user, const PointedThing &pointed) { - const char *modname = luaL_checkstring(L, 1); - // Do it - const ModSpec *mod = get_server(L)->getModSpec(modname); - if(!mod){ - lua_pushnil(L); - return 1; - } - lua_pushstring(L, mod->path.c_str()); - return 1; + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_item_callback(L, item.name.c_str(), "on_use")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectref_get_or_create(L, user); + push_pointed_thing(L, pointed); + if(lua_pcall(L, 3, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L, -1); + return true; } -// get_worldpath() -static int l_get_worldpath(lua_State *L) +bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node, + ServerActiveObject *puncher) { - std::string worldpath = get_server(L)->getWorldPath(); - lua_pushstring(L, worldpath.c_str()); - return 1; -} + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -static const struct luaL_Reg minetest_f [] = { - {"debug", l_debug}, - {"log", l_log}, - {"register_item_raw", l_register_item_raw}, - {"register_alias_raw", l_register_alias_raw}, - {"register_craft", l_register_craft}, - {"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_inventory", l_get_inventory}, - {"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_worldpath", l_get_worldpath}, - {NULL, NULL} -}; + INodeDefManager *ndef = get_server(L)->ndef(); + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_punch")) + return false; -/* - Main export function -*/ + // Call function + push_v3s16(L, p); + pushnode(L, node, ndef); + objectref_get_or_create(L, puncher); + if(lua_pcall(L, 3, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return true; +} -void scriptapi_export(lua_State *L, Server *server) +bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node, + ServerActiveObject *digger) { realitycheck(L); assert(lua_checkstack(L, 20)); - verbosestream<<"scriptapi_export()"<ndef(); - // Add tables to minetest - - lua_newtable(L); - lua_setfield(L, -2, "object_refs"); - lua_newtable(L); - lua_setfield(L, -2, "luaentities"); + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_dig")) + return false; - // Register wrappers - LuaItemStack::Register(L); - InvRef::Register(L); - NodeMetaRef::Register(L); - ObjectRef::Register(L); - EnvRef::Register(L); + // Call function + push_v3s16(L, p); + pushnode(L, node, ndef); + objectref_get_or_create(L, digger); + if(lua_pcall(L, 3, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return true; } -bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath, - const std::string &modname) +void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node) { - ModNameStorer modnamestorer(L, modname); + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz" - "0123456789_")){ - errorstream<<"Error loading mod \""<ndef(); - try{ - success = script_load(L, scriptpath.c_str()); - } - catch(LuaError &e){ - errorstream<<"Error loading mod \""<get(node).name.c_str(), "on_construct")) + return; - return success; + // Call function + push_v3s16(L, p); + if(lua_pcall(L, 1, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); } -void scriptapi_add_environment(lua_State *L, ServerEnvironment *env) +void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node) { realitycheck(L); assert(lua_checkstack(L, 20)); - verbosestream<<"scriptapi_add_environment"<ndef(); - // minetest.env = envref - lua_getglobal(L, "minetest"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushvalue(L, envref); - lua_setfield(L, -2, "env"); + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_destruct")) + return; - // Store environment as light userdata in registry - lua_pushlightuserdata(L, env); - lua_setfield(L, LUA_REGISTRYINDEX, "minetest_env"); + // Call function + push_v3s16(L, p); + if(lua_pcall(L, 1, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} - /* - Add ActiveBlockModifiers to environment - */ +void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - // Get minetest.registered_abms - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_abms"); - luaL_checktype(L, -1, LUA_TTABLE); - int registered_abms = lua_gettop(L); - - if(lua_istable(L, registered_abms)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - int id = lua_tonumber(L, -2); - int current_abm = lua_gettop(L); + INodeDefManager *ndef = get_server(L)->ndef(); - std::set trigger_contents; - lua_getfield(L, current_abm, "nodenames"); - 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 - luaL_checktype(L, -1, LUA_TSTRING); - trigger_contents.insert(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, -1)){ - trigger_contents.insert(lua_tostring(L, -1)); - } - lua_pop(L, 1); + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "after_destruct")) + return; - std::set required_neighbors; - lua_getfield(L, current_abm, "neighbors"); - 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 - luaL_checktype(L, -1, LUA_TSTRING); - required_neighbors.insert(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, -1)){ - required_neighbors.insert(lua_tostring(L, -1)); - } - lua_pop(L, 1); + // 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)); +} - float trigger_interval = 10.0; - getfloatfield(L, current_abm, "interval", trigger_interval); +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); - int trigger_chance = 50; - getintfield(L, current_abm, "chance", trigger_chance); + INodeDefManager *ndef = get_server(L)->ndef(); - LuaABM *abm = new LuaABM(L, id, trigger_contents, - required_neighbors, trigger_interval, trigger_chance); - - env->addActiveBlockModifier(abm); + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_timer")) + return false; - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } - lua_pop(L, 1); + // 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; } -#if 0 -// Dump stack top with the dump2 function -static void dump2(lua_State *L, const char *name) +void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, + const std::string &formname, + const std::map &fields, + ServerActiveObject *sender) { - // Dump object (debug) - lua_getglobal(L, "dump2"); - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_pushvalue(L, -2); // Get previous stack top as first parameter - lua_pushstring(L, name); - if(lua_pcall(L, 2, 0, 0)) + 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; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_receive_fields")) + return; + + // Call function + // param 1 + push_v3s16(L, p); + // 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); + } + // param 4 + objectref_get_or_create(L, sender); + if(lua_pcall(L, 4, 0, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); } -#endif /* - object_reference + Node metadata inventory callbacks */ -void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj) +// 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)); - //infostream<<"scriptapi_add_object_reference: id="<getId()<ndef(); - // Get minetest.object_refs table - lua_getglobal(L, "minetest"); - 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 - lua_settable(L, objectstable); + // 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); } -void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj) +// 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)); - //infostream<<"scriptapi_rm_object_reference: id="<getId()<getId()); // Push id - lua_gettable(L, objectstable); - // Set object reference to NULL - ObjectRef::set_null(L); - lua_pop(L, 1); // pop object + 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; - // Set object_refs[id] = nil - lua_pushnumber(L, cobj->getId()); // Push id - lua_pushnil(L); - lua_settable(L, objectstable); + // 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); } -bool scriptapi_on_chat_message(lua_State *L, const std::string &name, - const std::string &message) +// 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) { 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_chat_messages"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - lua_pushstring(L, name.c_str()); - lua_pushstring(L, message.c_str()); - if(lua_pcall(L, 2, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - bool ate = lua_toboolean(L, -1); - lua_pop(L, 1); - if(ate) - return true; - // value removed, keep key for next iteration - } - return false; -} + INodeDefManager *ndef = get_server(L)->ndef(); -/* - misc -*/ + // 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; -void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player) + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "on_metadata_inventory_move")) + 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)); +} + +// Report put items +void scriptapi_nodemeta_inventory_on_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); - // Get minetest.registered_on_newplayers - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_newplayers"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - objectref_get_or_create(L, player); - if(lua_pcall(L, 1, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - // value removed, keep key for next iteration - } -} + INodeDefManager *ndef = get_server(L)->ndef(); -void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_on_dieplayers - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_dieplayers"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - objectref_get_or_create(L, player); - if(lua_pcall(L, 1, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - // value removed, keep key for next iteration - } -} + // 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; + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "on_metadata_inventory_put")) + return; -bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *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)); +} + +// 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); assert(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); - dstream<<"player: "<getId()<ndef(); - bool positioning_handled_by_some = false; + // 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; - // Get minetest.registered_on_respawnplayers - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_respawnplayers"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - objectref_get_or_create(L, player); - if(lua_pcall(L, 1, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - bool positioning_handled = lua_toboolean(L, -1); - lua_pop(L, 1); - if(positioning_handled) - positioning_handled_by_some = true; - // value removed, keep key for next iteration - } - return positioning_handled_by_some; -} + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "on_metadata_inventory_take")) + return; -void scriptapi_get_creative_inventory(lua_State *L, ServerRemotePlayer *player) -{ - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "creative_inventory"); - luaL_checktype(L, -1, LUA_TTABLE); - inventory_set_list_from_lua(&player->inventory, "main", L, -1, - PLAYER_INVENTORY_SIZE); + // 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)); } /* - item callbacks and node callbacks + Detached inventory callbacks */ -// Retrieves minetest.registered_items[name][callbackname] +// 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_item_callback(lua_State *L, - const char *name, const char *callbackname) +static bool get_detached_inventory_callback(lua_State *L, + const std::string &name, const char *callbackname) { lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_items"); + lua_getfield(L, -1, "detached_inventories"); lua_remove(L, -2); luaL_checktype(L, -1, LUA_TTABLE); - lua_getfield(L, -1, name); + lua_getfield(L, -1, name.c_str()); lua_remove(L, -2); // Should be a table if(lua_type(L, -1) != LUA_TTABLE) @@ -4048,121 +6512,216 @@ static bool get_item_callback(lua_State *L, } else { - errorstream<<"Item \""<ndef(); - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_punch")) - return false; + if(!get_detached_inventory_callback(L, name, "on_move")) + return; - // Call function - push_v3s16(L, pos); - pushnode(L, node, ndef); - objectref_get_or_create(L, puncher); - if(lua_pcall(L, 3, 0, 0)) + // function(inv, from_list, from_index, to_list, to_index, count, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // 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)); - return true; } -bool scriptapi_node_on_dig(lua_State *L, v3s16 pos, MapNode node, - ServerActiveObject *digger) +// Report put items +void scriptapi_detached_inventory_on_put(lua_State *L, + const std::string &name, + 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(); + // Push callback function on stack + if(!get_detached_inventory_callback(L, name, "on_put")) + return; + + // Call function(inv, listname, index, stack, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // 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)); +} + +// Report taken items +void scriptapi_detached_inventory_on_take(lua_State *L, + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_dig")) - return false; + if(!get_detached_inventory_callback(L, name, "on_take")) + return; - // Call function - push_v3s16(L, pos); - pushnode(L, node, ndef); - objectref_get_or_create(L, digger); - if(lua_pcall(L, 3, 0, 0)) + // Call function(inv, listname, index, stack, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // 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)); - return true; } /* @@ -4179,22 +6738,13 @@ void scriptapi_environment_step(lua_State *L, float dtime) // Get minetest.registered_globalsteps lua_getglobal(L, "minetest"); lua_getfield(L, -1, "registered_globalsteps"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - lua_pushnumber(L, dtime); - if(lua_pcall(L, 1, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - // value removed, keep key for next iteration - } + // Call callbacks + lua_pushnumber(L, dtime); + scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); } -void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp) +void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp, + u32 blockseed) { realitycheck(L); assert(lua_checkstack(L, 20)); @@ -4204,20 +6754,11 @@ void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp) // Get minetest.registered_on_generateds lua_getglobal(L, "minetest"); lua_getfield(L, -1, "registered_on_generateds"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - push_v3s16(L, minp); - push_v3s16(L, maxp); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - // value removed, keep key for next iteration - } + // Call callbacks + push_v3s16(L, minp); + push_v3s16(L, maxp); + lua_pushnumber(L, blockseed); + scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST); } /* @@ -4231,7 +6772,7 @@ bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name) verbosestream<<"scriptapi_luaentity_add: id="<hp_max = 10; + /* Read stuff */ - + prop->hp_max = getintfield_default(L, -1, "hp_max", 10); getboolfield(L, -1, "physical", prop->physical); @@ -4371,41 +6916,19 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id, 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); - - lua_getfield(L, -1, "visual_size"); - if(lua_istable(L, -1)) - prop->visual_size = read_v2f(L, -1); - lua_pop(L, 1); - lua_getfield(L, -1, "textures"); - if(lua_istable(L, -1)){ - prop->textures.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->textures.push_back(lua_tostring(L, -1)); - else - prop->textures.push_back(""); - // 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)) - prop->spritediv = read_v2s16(L, -1); - lua_pop(L, 1); + getstringfield(L, -1, "mesh", prop->mesh); - lua_getfield(L, -1, "initial_sprite_basepos"); - if(lua_istable(L, -1)) - prop->initial_sprite_basepos = read_v2s16(L, -1); + // 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); lua_pop(L, 1); }