]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/scriptapi.cpp
Add EnvRef:get_objects_inside_radius(pos, radius)
[dragonfireclient.git] / src / scriptapi.cpp
index ce31584a37b13f4bc96dac39f93a41e545eee4e8..1a50e1e34e1571854c11323e229a2f5066d1a07b 100644 (file)
@@ -107,6 +107,103 @@ class StackUnroller
        }
 };
 
+class ModNameStorer
+{
+private:
+       lua_State *L;
+public:
+       ModNameStorer(lua_State *L_, const std::string modname):
+               L(L_)
+       {
+               // Store current modname in registry
+               lua_pushstring(L, modname.c_str());
+               lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
+       }
+       ~ModNameStorer()
+       {
+               // Clear current modname in registry
+               lua_pushnil(L);
+               lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
+       }
+};
+
+std::string get_current_modname(lua_State *L)
+{
+       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
+       std::string modname = "";
+       if(lua_type(L, -1) == LUA_TSTRING)
+               modname = lua_tostring(L, -1);
+       lua_pop(L, 1);
+       return modname;
+}
+
+void check_modname_prefix(lua_State *L, std::string &name)
+{
+       if(name.size() == 0)
+               throw LuaError(L, std::string("Name is empty"));
+       
+       if(name[0] == ':'){
+               name = name.substr(1);
+               return;
+       }
+       
+       std::string modname = get_current_modname(L);
+       assert(modname != "");
+       
+       // For __builtin, anything goes
+       if(modname == "__builtin")
+               return;
+       
+       if(name.substr(0, modname.size()+1) != modname + ":")
+               throw LuaError(L, std::string("Name \"")+name
+                               +"\" does not follow naming conventions: "
+                               +"\"modname:\" or \":\" prefix required)");
+       
+       std::string subname = name.substr(modname.size()+1);
+       if(!string_allowed(subname, "abcdefghijklmnopqrstuvwxyz"
+                       "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"))
+               throw LuaError(L, std::string("Name \"")+name
+                               +"\" does not follow naming conventions: "
+                               +"\"contains unallowed characters");
+}
+
+static void push_v3f(lua_State *L, v3f p)
+{
+       lua_newtable(L);
+       lua_pushnumber(L, p.X);
+       lua_setfield(L, -2, "x");
+       lua_pushnumber(L, p.Y);
+       lua_setfield(L, -2, "y");
+       lua_pushnumber(L, p.Z);
+       lua_setfield(L, -2, "z");
+}
+
+static v2s16 read_v2s16(lua_State *L, int index)
+{
+       v2s16 p;
+       luaL_checktype(L, index, LUA_TTABLE);
+       lua_getfield(L, index, "x");
+       p.X = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       lua_getfield(L, index, "y");
+       p.Y = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       return p;
+}
+
+static v2f read_v2f(lua_State *L, int index)
+{
+       v2f p;
+       luaL_checktype(L, index, LUA_TTABLE);
+       lua_getfield(L, index, "x");
+       p.X = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       lua_getfield(L, index, "y");
+       p.Y = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       return p;
+}
+
 static v3f readFloatPos(lua_State *L, int index)
 {
        v3f pos;
@@ -127,13 +224,7 @@ static v3f readFloatPos(lua_State *L, int index)
 static void pushFloatPos(lua_State *L, v3f p)
 {
        p /= BS;
-       lua_newtable(L);
-       lua_pushnumber(L, p.X);
-       lua_setfield(L, -2, "x");
-       lua_pushnumber(L, p.Y);
-       lua_setfield(L, -2, "y");
-       lua_pushnumber(L, p.Z);
-       lua_setfield(L, -2, "z");
+       push_v3f(L, p);
 }
 
 static void pushpos(lua_State *L, v3s16 p)
@@ -168,7 +259,7 @@ static void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef)
 static MapNode readnode(lua_State *L, int index, INodeDefManager *ndef)
 {
        lua_getfield(L, index, "name");
-       const char *name = lua_tostring(L, -1);
+       const char *name = luaL_checkstring(L, -1);
        lua_pop(L, 1);
        u8 param1;
        lua_getfield(L, index, "param1");
@@ -233,32 +324,6 @@ static core::aabbox3d<f32> read_aabbox3df32(lua_State *L, int index, f32 scale)
        return box;
 }
 
-static v2s16 read_v2s16(lua_State *L, int index)
-{
-       v2s16 p;
-       luaL_checktype(L, index, LUA_TTABLE);
-       lua_getfield(L, index, "x");
-       p.X = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       lua_getfield(L, index, "y");
-       p.Y = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       return p;
-}
-
-static v2f read_v2f(lua_State *L, int index)
-{
-       v2f p;
-       luaL_checktype(L, index, LUA_TTABLE);
-       lua_getfield(L, index, "x");
-       p.X = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       lua_getfield(L, index, "y");
-       p.Y = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       return p;
-}
-
 static bool getstringfield(lua_State *L, int table,
                const char *fieldname, std::string &result)
 {
@@ -386,13 +451,36 @@ static int getenumfield(lua_State *L, int table,
        return result;
 }
 
+static void setfloatfield(lua_State *L, int table,
+               const char *fieldname, float value)
+{
+       lua_pushnumber(L, value);
+       if(table < 0)
+               table -= 1;
+       lua_setfield(L, table, fieldname);
+}
+
+static void warn_if_field_exists(lua_State *L, int table,
+               const char *fieldname, const std::string &message)
+{
+       lua_getfield(L, table, fieldname);
+       if(!lua_isnil(L, -1)){
+               infostream<<script_get_backtrace(L)<<std::endl;
+               infostream<<"WARNING: field \""<<fieldname<<"\": "
+                               <<message<<std::endl;
+       }
+       lua_pop(L, 1);
+}
+
 /*
        Inventory stuff
 */
 
 static void inventory_set_list_from_lua(Inventory *inv, const char *name,
-               lua_State *L, int tableindex, IGameDef *gamedef)
+               lua_State *L, int tableindex, IGameDef *gamedef, int forcesize=-1)
 {
+       if(tableindex < 0)
+               tableindex = lua_gettop(L) + 1 + tableindex;
        // If nil, delete list
        if(lua_isnil(L, tableindex)){
                inv->deleteList(name);
@@ -406,15 +494,18 @@ static void inventory_set_list_from_lua(Inventory *inv, const char *name,
        while(lua_next(L, table) != 0){
                // key at index -2 and value at index -1
                luaL_checktype(L, -1, LUA_TSTRING);
-               std::string itemstring = lua_tostring(L, -1);
+               std::string itemstring = luaL_checkstring(L, -1);
                items.push_back(itemstring);
                // removes value, keeps key for next iteration
                lua_pop(L, 1);
        }
-       InventoryList *invlist = inv->addList(name, items.size());
+       int listsize = (forcesize != -1) ? forcesize : items.size();
+       InventoryList *invlist = inv->addList(name, listsize);
        int index = 0;
        for(std::list<std::string>::const_iterator
                        i = items.begin(); i != items.end(); i++){
+               if(forcesize != -1 && index == forcesize)
+                       break;
                const std::string &itemstring = *i;
                InventoryItem *newitem = NULL;
                if(itemstring != "")
@@ -424,6 +515,11 @@ static void inventory_set_list_from_lua(Inventory *inv, const char *name,
                delete olditem;
                index++;
        }
+       while(forcesize != -1 && index < forcesize){
+               InventoryItem *olditem = invlist->changeItem(index, NULL);
+               delete olditem;
+               index++;
+       }
 }
 
 static void inventory_get_list_to_lua(Inventory *inv, const char *name,
@@ -446,15 +542,80 @@ static void inventory_get_list_to_lua(Inventory *inv, const char *name,
                lua_pushvalue(L, table_insert);
                lua_pushvalue(L, table);
                if(item == NULL){
-                       lua_pushnil(L);
+                       lua_pushstring(L, "");
                } else {
                        lua_pushstring(L, item->getItemString().c_str());
                }
                if(lua_pcall(L, 2, 0, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
        }
 }
 
+/*
+       ToolDiggingProperties
+*/
+
+static ToolDiggingProperties read_tool_digging_properties(
+               lua_State *L, int table)
+{
+       ToolDiggingProperties prop;
+       getfloatfield(L, table, "full_punch_interval", prop.full_punch_interval);
+       getfloatfield(L, table, "basetime", prop.basetime);
+       getfloatfield(L, table, "dt_weight", prop.dt_weight);
+       getfloatfield(L, table, "dt_crackiness", prop.dt_crackiness);
+       getfloatfield(L, table, "dt_crumbliness", prop.dt_crumbliness);
+       getfloatfield(L, table, "dt_cuttability", prop.dt_cuttability);
+       getfloatfield(L, table, "basedurability", prop.basedurability);
+       getfloatfield(L, table, "dd_weight", prop.dd_weight);
+       getfloatfield(L, table, "dd_crackiness", prop.dd_crackiness);
+       getfloatfield(L, table, "dd_crumbliness", prop.dd_crumbliness);
+       getfloatfield(L, table, "dd_cuttability", prop.dd_cuttability);
+       return prop;
+}
+
+static void set_tool_digging_properties(lua_State *L, int table,
+               const ToolDiggingProperties &prop)
+{
+       setfloatfield(L, table, "full_punch_interval", prop.full_punch_interval);
+       setfloatfield(L, table, "basetime", prop.basetime);
+       setfloatfield(L, table, "dt_weight", prop.dt_weight);
+       setfloatfield(L, table, "dt_crackiness", prop.dt_crackiness);
+       setfloatfield(L, table, "dt_crumbliness", prop.dt_crumbliness);
+       setfloatfield(L, table, "dt_cuttability", prop.dt_cuttability);
+       setfloatfield(L, table, "basedurability", prop.basedurability);
+       setfloatfield(L, table, "dd_weight", prop.dd_weight);
+       setfloatfield(L, table, "dd_crackiness", prop.dd_crackiness);
+       setfloatfield(L, table, "dd_crumbliness", prop.dd_crumbliness);
+       setfloatfield(L, table, "dd_cuttability", prop.dd_cuttability);
+}
+
+static void push_tool_digging_properties(lua_State *L,
+               const ToolDiggingProperties &prop)
+{
+       lua_newtable(L);
+       set_tool_digging_properties(L, -1, prop);
+}
+
+/*
+       ToolDefinition
+*/
+
+static ToolDefinition read_tool_definition(lua_State *L, int table)
+{
+       ToolDefinition def;
+       getstringfield(L, table, "image", def.imagename);
+       def.properties = read_tool_digging_properties(L, table);
+       return def;
+}
+
+static void push_tool_definition(lua_State *L, const ToolDefinition &def)
+{
+       lua_newtable(L);
+       lua_pushstring(L, def.imagename.c_str());
+       lua_setfield(L, -2, "image");
+       set_tool_digging_properties(L, -1, def.properties);
+}
+
 /*
        EnumString definitions
 */
@@ -527,8 +688,9 @@ static int l_register_nodedef_defaults(lua_State *L)
 // register_entity(name, prototype)
 static int l_register_entity(lua_State *L)
 {
-       const char *name = luaL_checkstring(L, 1);
-       infostream<<"register_entity: "<<name<<std::endl;
+       std::string name = luaL_checkstring(L, 1);
+       check_modname_prefix(L, name);
+       //infostream<<"register_entity: "<<name<<std::endl;
        luaL_checktype(L, 2, LUA_TTABLE);
 
        // Get minetest.registered_entities
@@ -538,7 +700,7 @@ static int l_register_entity(lua_State *L)
        int registered_entities = lua_gettop(L);
        lua_pushvalue(L, 2); // Object = param 2 -> stack top
        // registered_entities[name] = object
-       lua_setfield(L, registered_entities, name);
+       lua_setfield(L, registered_entities, name.c_str());
        
        // Get registered object to top of stack
        lua_pushvalue(L, 2);
@@ -564,15 +726,18 @@ class LuaABM : public ActiveBlockModifier
        int m_id;
 
        std::set<std::string> m_trigger_contents;
+       std::set<std::string> m_required_neighbors;
        float m_trigger_interval;
        u32 m_trigger_chance;
 public:
        LuaABM(lua_State *L, int id,
                        const std::set<std::string> &trigger_contents,
+                       const std::set<std::string> &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)
        {
@@ -581,6 +746,10 @@ class LuaABM : public ActiveBlockModifier
        {
                return m_trigger_contents;
        }
+       virtual std::set<std::string> getRequiredNeighbors()
+       {
+               return m_required_neighbors;
+       }
        virtual float getTriggerInterval()
        {
                return m_trigger_interval;
@@ -619,14 +788,14 @@ class LuaABM : public ActiveBlockModifier
                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\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
        }
 };
 
 // register_abm({...})
 static int l_register_abm(lua_State *L)
 {
-       infostream<<"register_abm"<<std::endl;
+       //infostream<<"register_abm"<<std::endl;
        luaL_checktype(L, 1, LUA_TTABLE);
 
        // Get minetest.registered_abms
@@ -660,8 +829,9 @@ static int l_register_abm(lua_State *L)
 // register_tool(name, {lots of stuff})
 static int l_register_tool(lua_State *L)
 {
-       const char *name = luaL_checkstring(L, 1);
-       infostream<<"register_tool: "<<name<<std::endl;
+       std::string name = luaL_checkstring(L, 1);
+       check_modname_prefix(L, name);
+       //infostream<<"register_tool: "<<name<<std::endl;
        luaL_checktype(L, 2, LUA_TTABLE);
        int table = 2;
 
@@ -672,19 +842,7 @@ static int l_register_tool(lua_State *L)
        IWritableToolDefManager *tooldef =
                        server->getWritableToolDefManager();
        
-       ToolDefinition def;
-       
-       getstringfield(L, table, "image", def.imagename);
-       getfloatfield(L, table, "basetime", def.properties.basetime);
-       getfloatfield(L, table, "dt_weight", def.properties.dt_weight);
-       getfloatfield(L, table, "dt_crackiness", def.properties.dt_crackiness);
-       getfloatfield(L, table, "dt_crumbliness", def.properties.dt_crumbliness);
-       getfloatfield(L, table, "dt_cuttability", def.properties.dt_cuttability);
-       getfloatfield(L, table, "basedurability", def.properties.basedurability);
-       getfloatfield(L, table, "dd_weight", def.properties.dd_weight);
-       getfloatfield(L, table, "dd_crackiness", def.properties.dd_crackiness);
-       getfloatfield(L, table, "dd_crumbliness", def.properties.dd_crumbliness);
-       getfloatfield(L, table, "dd_cuttability", def.properties.dd_cuttability);
+       ToolDefinition def = read_tool_definition(L, table);
 
        tooldef->registerTool(name, def);
        return 0; /* number of results */
@@ -693,8 +851,9 @@ static int l_register_tool(lua_State *L)
 // register_craftitem(name, {lots of stuff})
 static int l_register_craftitem(lua_State *L)
 {
-       const char *name = luaL_checkstring(L, 1);
-       infostream<<"register_craftitem: "<<name<<std::endl;
+       std::string name = luaL_checkstring(L, 1);
+       check_modname_prefix(L, name);
+       //infostream<<"register_craftitem: "<<name<<std::endl;
        luaL_checktype(L, 2, LUA_TTABLE);
        int table = 2;
 
@@ -718,7 +877,7 @@ static int l_register_craftitem(lua_State *L)
        CraftItemDefinition def;
        
        getstringfield(L, table, "image", def.imagename);
-       getstringfield(L, table, "cookresult_item", def.cookresult_item);
+       getstringfield(L, table, "cookresult_itemstring", def.cookresult_item);
        getfloatfield(L, table, "furnace_cooktime", def.furnace_cooktime);
        getfloatfield(L, table, "furnace_burntime", def.furnace_burntime);
        def.usable = getboolfield_default(L, table, "usable", got_on_use);
@@ -734,7 +893,7 @@ static int l_register_craftitem(lua_State *L)
        craftitemdef->registerCraftItem(name, def);
 
        lua_pushvalue(L, table);
-       scriptapi_add_craftitem(L, name);
+       scriptapi_add_craftitem(L, name.c_str());
 
        return 0; /* number of results */
 }
@@ -742,8 +901,9 @@ static int l_register_craftitem(lua_State *L)
 // register_node(name, {lots of stuff})
 static int l_register_node(lua_State *L)
 {
-       const char *name = luaL_checkstring(L, 1);
-       infostream<<"register_node: "<<name<<std::endl;
+       std::string name = luaL_checkstring(L, 1);
+       check_modname_prefix(L, name);
+       //infostream<<"register_node: "<<name<<std::endl;
        luaL_checktype(L, 2, LUA_TTABLE);
        int nodedef_table = 2;
 
@@ -777,7 +937,7 @@ static int l_register_node(lua_State *L)
        lua_getglobal(L, "minetest");
        lua_getfield(L, -1, "registered_nodes");
        luaL_checktype(L, -1, LUA_TTABLE);
-       lua_pushstring(L, name);
+       lua_pushstring(L, name.c_str());
        lua_pushvalue(L, nodedef_table);
        lua_settable(L, -3);
 
@@ -876,7 +1036,9 @@ static int l_register_node(lua_State *L)
        
        // True for all ground-like things like stone and mud, false for eg. trees
        getboolfield(L, nodedef_table, "is_ground_content", f.is_ground_content);
-       getboolfield(L, nodedef_table, "light_propagates", f.light_propagates);
+       f.light_propagates = (f.param_type == CPT_LIGHT);
+       warn_if_field_exists(L, nodedef_table, "light_propagates",
+                       "deprecated: determined from paramtype");
        getboolfield(L, nodedef_table, "sunlight_propagates", f.sunlight_propagates);
        // This is used for collision detection.
        // Also for general solidness queries.
@@ -966,7 +1128,7 @@ static int l_register_node(lua_State *L)
        }
        lua_pop(L, 1);
 
-       getstringfield(L, nodedef_table, "cookresult_item", f.cookresult_item);
+       getstringfield(L, nodedef_table, "cookresult_itemstring", f.cookresult_item);
        getfloatfield(L, nodedef_table, "furnace_cooktime", f.furnace_cooktime);
        getfloatfield(L, nodedef_table, "furnace_burntime", f.furnace_burntime);
        
@@ -979,10 +1141,64 @@ static int l_register_node(lua_State *L)
        return 0; /* number of results */
 }
 
+// alias_node(name, convert_to_name)
+static int l_alias_node(lua_State *L)
+{
+       std::string name = luaL_checkstring(L, 1);
+       std::string convert_to = luaL_checkstring(L, 2);
+
+       // Get server from registry
+       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
+       Server *server = (Server*)lua_touserdata(L, -1);
+       // And get the writable node definition manager from the server
+       IWritableNodeDefManager *nodedef =
+                       server->getWritableNodeDefManager();
+       
+       nodedef->setAlias(name, convert_to);
+       
+       return 0; /* number of results */
+}
+
+// alias_tool(name, convert_to_name)
+static int l_alias_tool(lua_State *L)
+{
+       std::string name = luaL_checkstring(L, 1);
+       std::string convert_to = luaL_checkstring(L, 2);
+
+       // Get server from registry
+       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
+       Server *server = (Server*)lua_touserdata(L, -1);
+       // And get the writable tool definition manager from the server
+       IWritableToolDefManager *tooldef =
+                       server->getWritableToolDefManager();
+       
+       tooldef->setAlias(name, convert_to);
+
+       return 0; /* number of results */
+}
+
+// alias_craftitem(name, convert_to_name)
+static int l_alias_craftitem(lua_State *L)
+{
+       std::string name = luaL_checkstring(L, 1);
+       std::string convert_to = luaL_checkstring(L, 2);
+
+       // Get server from registry
+       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
+       Server *server = (Server*)lua_touserdata(L, -1);
+       // And get the writable CraftItem definition manager from the server
+       IWritableCraftItemDefManager *craftitemdef =
+                       server->getWritableCraftItemDefManager();
+       
+       craftitemdef->setAlias(name, convert_to);
+
+       return 0; /* number of results */
+}
+
 // register_craft({output=item, recipe={{item00,item10},{item01,item11}})
 static int l_register_craft(lua_State *L)
 {
-       infostream<<"register_craft"<<std::endl;
+       //infostream<<"register_craft"<<std::endl;
        luaL_checktype(L, 1, LUA_TTABLE);
        int table0 = 1;
 
@@ -1029,7 +1245,10 @@ static int l_register_craft(lua_State *L)
                                width = colcount;
                        } else {
                                if(colcount != width){
-                                       script_error(L, "error: %s\n", "Invalid crafting recipe");
+                                       std::string error;
+                                       error += "Invalid crafting recipe (output=\""
+                                                       + output + "\")";
+                                       throw LuaError(L, error);
                                }
                        }
                        // removes value, keeps key for next iteration
@@ -1120,6 +1339,23 @@ static int l_get_player_privs(lua_State *L)
        return 1;
 }
 
+// get_modpath(modname)
+static int l_get_modpath(lua_State *L)
+{
+       const char *modname = luaL_checkstring(L, 1);
+       // Get server from registry
+       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
+       Server *server = (Server*)lua_touserdata(L, -1);
+       // Do it
+       const ModSpec *mod = server->getModSpec(modname);
+       if(!mod){
+               lua_pushnil(L);
+               return 1;
+       }
+       lua_pushstring(L, mod->path.c_str());
+       return 1;
+}
+
 static const struct luaL_Reg minetest_f [] = {
        {"register_nodedef_defaults", l_register_nodedef_defaults},
        {"register_entity", l_register_entity},
@@ -1128,11 +1364,15 @@ static const struct luaL_Reg minetest_f [] = {
        {"register_node", l_register_node},
        {"register_craft", l_register_craft},
        {"register_abm", l_register_abm},
+       {"alias_node", l_alias_node},
+       {"alias_tool", l_alias_tool},
+       {"alias_craftitem", l_alias_craftitem},
        {"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_modpath", l_get_modpath},
        {NULL, NULL}
 };
 
@@ -1271,7 +1511,7 @@ class NodeMetaRef
                NodeMetadata *meta = getmeta(ref);
                if(meta == NULL) return 0;
                // Do it
-               std::string text = lua_tostring(L, 2);
+               std::string text = luaL_checkstring(L, 2);
                meta->setText(text);
                reportMetadataChange(ref);
                return 0;
@@ -1311,7 +1551,7 @@ class NodeMetaRef
                NodeMetadata *meta = getmeta(ref);
                if(meta == NULL) return 0;
                // Do it
-               std::string text = lua_tostring(L, 2);
+               std::string text = luaL_checkstring(L, 2);
                meta->setInfoText(text);
                reportMetadataChange(ref);
                return 0;
@@ -1325,7 +1565,7 @@ class NodeMetaRef
                if(meta == NULL) return 0;
                // Do it
                Inventory *inv = meta->getInventory();
-               const char *name = lua_tostring(L, 2);
+               const char *name = luaL_checkstring(L, 2);
                inventory_set_list_from_lua(inv, name, L, 3,
                                ref->m_env->getGameDef());
                reportMetadataChange(ref);
@@ -1340,7 +1580,7 @@ class NodeMetaRef
                if(meta == NULL) return 0;
                // Do it
                Inventory *inv = meta->getInventory();
-               const char *name = lua_tostring(L, 2);
+               const char *name = luaL_checkstring(L, 2);
                inventory_get_list_to_lua(inv, name, L);
                return 1;
        }
@@ -1352,7 +1592,7 @@ class NodeMetaRef
                NodeMetadata *meta = getmeta(ref);
                if(meta == NULL) return 0;
                // Do it
-               std::string text = lua_tostring(L, 2);
+               std::string text = luaL_checkstring(L, 2);
                meta->setInventoryDrawSpec(text);
                reportMetadataChange(ref);
                return 0;
@@ -1450,7 +1690,7 @@ class NodeMetaRef
                NodeMetadata *meta = getmeta(ref);
                if(meta == NULL) return 0;
                // Do it
-               std::string name = lua_tostring(L, 2);
+               std::string name = luaL_checkstring(L, 2);
                size_t len = 0;
                const char *s = lua_tolstring(L, 3, &len);
                std::string str(s, len);
@@ -1466,7 +1706,7 @@ class NodeMetaRef
                NodeMetadata *meta = getmeta(ref);
                if(meta == NULL) return 0;
                // Do it
-               std::string name = lua_tostring(L, 2);
+               std::string name = luaL_checkstring(L, 2);
                std::string str = meta->getString(name);
                lua_pushlstring(L, str.c_str(), str.size());
                return 1;
@@ -1688,65 +1928,16 @@ class ObjectRef
                return 0;
        }
 
-       // get_wielded_itemstring(self)
-       static int l_get_wielded_itemstring(lua_State *L)
-       {
-               ObjectRef *ref = checkobject(L, 1);
-               ServerActiveObject *co = getobject(ref);
-               if(co == NULL) return 0;
-               // Do it
-               InventoryItem *item = co->getWieldedItem();
-               if(item == NULL){
-                       lua_pushnil(L);
-                       return 1;
-               }
-               lua_pushstring(L, item->getItemString().c_str());
-               return 1;
-       }
-
-       // get_wielded_item(self)
-       static int l_get_wielded_item(lua_State *L)
+       // get_wield_digging_properties(self)
+       static int l_get_wield_digging_properties(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
                // Do it
-               InventoryItem *item0 = co->getWieldedItem();
-               if(item0 == NULL){
-                       lua_pushnil(L);
-                       return 1;
-               }
-               if(std::string("MaterialItem") == item0->getName()){
-                       MaterialItem *item = (MaterialItem*)item0;
-                       lua_newtable(L);
-                       lua_pushstring(L, "NodeItem");
-                       lua_setfield(L, -2, "type");
-                       lua_pushstring(L, item->getNodeName().c_str());
-                       lua_setfield(L, -2, "name");
-               }
-               else if(std::string("CraftItem") == item0->getName()){
-                       CraftItem *item = (CraftItem*)item0;
-                       lua_newtable(L);
-                       lua_pushstring(L, "CraftItem");
-                       lua_setfield(L, -2, "type");
-                       lua_pushstring(L, item->getSubName().c_str());
-                       lua_setfield(L, -2, "name");
-               }
-               else if(std::string("ToolItem") == item0->getName()){
-                       ToolItem *item = (ToolItem*)item0;
-                       lua_newtable(L);
-                       lua_pushstring(L, "ToolItem");
-                       lua_setfield(L, -2, "type");
-                       lua_pushstring(L, item->getToolName().c_str());
-                       lua_setfield(L, -2, "name");
-                       lua_pushstring(L, itos(item->getWear()).c_str());
-                       lua_setfield(L, -2, "wear");
-               }
-               else{
-                       errorstream<<"l_get_wielded_item: Unknown item name: \""
-                                       <<item0->getName()<<"\""<<std::endl;
-                       lua_pushnil(L);
-               }
+               ToolDiggingProperties prop;
+               co->getWieldDiggingProperties(&prop);
+               push_tool_digging_properties(L, prop);
                return 1;
        }
 
@@ -1771,7 +1962,7 @@ class ObjectRef
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
                // itemstring
-               const char *itemstring = lua_tostring(L, 2);
+               const char *itemstring = luaL_checkstring(L, 2);
                infostream<<"ObjectRef::l_add_to_inventory(): id="<<co->getId()
                                <<" itemstring=\""<<itemstring<<"\""<<std::endl;
                // Do it
@@ -1787,7 +1978,7 @@ class ObjectRef
                        // Return
                        lua_pushboolean(L, added);
                        if(!added)
-                               lua_pushstring(L, "does not fit");
+                               lua_pushstring(L, "failed to add item");
                        return 2;
                } catch(SerializationError &e){
                        // Return
@@ -1807,7 +1998,7 @@ class ObjectRef
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
                // itemstring
-               const char *itemstring = lua_tostring(L, 2);
+               const char *itemstring = luaL_checkstring(L, 2);
                infostream<<"ObjectRef::l_add_to_inventory_later(): id="<<co->getId()
                                <<" itemstring=\""<<itemstring<<"\""<<std::endl;
                // Do it
@@ -1903,7 +2094,7 @@ class ObjectRef
                LuaEntitySAO *co = getluaobject(ref);
                if(co == NULL) return 0;
                // Do it
-               std::string mod = lua_tostring(L, 2);
+               std::string mod = luaL_checkstring(L, 2);
                co->setTextureMod(mod);
                return 0;
        }
@@ -1954,10 +2145,10 @@ class ObjectRef
                ObjectRef *ref = checkobject(L, 1);
                ServerRemotePlayer *player = getplayer(ref);
                if(player == NULL) return 0;
-               const char *name = lua_tostring(L, 2);
+               const char *name = luaL_checkstring(L, 2);
                // Do it
                inventory_set_list_from_lua(&player->inventory, name, L, 3,
-                               player->getEnv()->getGameDef());
+                               player->getEnv()->getGameDef(), PLAYER_INVENTORY_SIZE);
                player->m_inventory_not_sent = true;
                return 0;
        }
@@ -1968,12 +2159,110 @@ class ObjectRef
                ObjectRef *ref = checkobject(L, 1);
                ServerRemotePlayer *player = getplayer(ref);
                if(player == NULL) return 0;
-               const char *name = lua_tostring(L, 2);
+               const char *name = luaL_checkstring(L, 2);
                // Do it
                inventory_get_list_to_lua(&player->inventory, name, L);
                return 1;
        }
 
+       // get_wielded_itemstring(self)
+       static int l_get_wielded_itemstring(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               ServerRemotePlayer *player = getplayer(ref);
+               if(player == NULL) return 0;
+               // Do it
+               InventoryItem *item = player->getWieldedItem();
+               if(item == NULL){
+                       lua_pushnil(L);
+                       return 1;
+               }
+               lua_pushstring(L, item->getItemString().c_str());
+               return 1;
+       }
+
+       // get_wielded_item(self)
+       static int l_get_wielded_item(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               ServerRemotePlayer *player = getplayer(ref);
+               if(player == NULL) return 0;
+               // Do it
+               InventoryItem *item0 = player->getWieldedItem();
+               if(item0 == NULL){
+                       lua_pushnil(L);
+                       return 1;
+               }
+               if(std::string("MaterialItem") == item0->getName()){
+                       MaterialItem *item = (MaterialItem*)item0;
+                       lua_newtable(L);
+                       lua_pushstring(L, "NodeItem");
+                       lua_setfield(L, -2, "type");
+                       lua_pushstring(L, item->getNodeName().c_str());
+                       lua_setfield(L, -2, "name");
+               }
+               else if(std::string("CraftItem") == item0->getName()){
+                       CraftItem *item = (CraftItem*)item0;
+                       lua_newtable(L);
+                       lua_pushstring(L, "CraftItem");
+                       lua_setfield(L, -2, "type");
+                       lua_pushstring(L, item->getSubName().c_str());
+                       lua_setfield(L, -2, "name");
+               }
+               else if(std::string("ToolItem") == item0->getName()){
+                       ToolItem *item = (ToolItem*)item0;
+                       lua_newtable(L);
+                       lua_pushstring(L, "ToolItem");
+                       lua_setfield(L, -2, "type");
+                       lua_pushstring(L, item->getToolName().c_str());
+                       lua_setfield(L, -2, "name");
+                       lua_pushstring(L, itos(item->getWear()).c_str());
+                       lua_setfield(L, -2, "wear");
+               }
+               else{
+                       errorstream<<"l_get_wielded_item: Unknown item name: \""
+                                       <<item0->getName()<<"\""<<std::endl;
+                       lua_pushnil(L);
+               }
+               return 1;
+       }
+
+       // get_look_dir(self)
+       static int l_get_look_dir(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               ServerRemotePlayer *player = getplayer(ref);
+               if(player == NULL) return 0;
+               // Do it
+               float pitch = player->getRadPitch();
+               float yaw = player->getRadYaw();
+               v3f v(cos(pitch)*cos(yaw), sin(pitch), cos(pitch)*sin(yaw));
+               push_v3f(L, v);
+               return 1;
+       }
+
+       // get_look_pitch(self)
+       static int l_get_look_pitch(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               ServerRemotePlayer *player = getplayer(ref);
+               if(player == NULL) return 0;
+               // Do it
+               lua_pushnumber(L, player->getRadPitch());
+               return 1;
+       }
+
+       // get_look_yaw(self)
+       static int l_get_look_yaw(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               ServerRemotePlayer *player = getplayer(ref);
+               if(player == NULL) return 0;
+               // Do it
+               lua_pushnumber(L, player->getRadYaw());
+               return 1;
+       }
+
 public:
        ObjectRef(ServerActiveObject *object):
                m_object(object)
@@ -2044,8 +2333,7 @@ const luaL_reg ObjectRef::methods[] = {
        method(ObjectRef, moveto),
        method(ObjectRef, punch),
        method(ObjectRef, right_click),
-       method(ObjectRef, get_wielded_itemstring),
-       method(ObjectRef, get_wielded_item),
+       method(ObjectRef, get_wield_digging_properties),
        method(ObjectRef, damage_wielded_item),
        method(ObjectRef, add_to_inventory),
        method(ObjectRef, add_to_inventory_later),
@@ -2061,6 +2349,11 @@ const luaL_reg ObjectRef::methods[] = {
        method(ObjectRef, get_player_name),
        method(ObjectRef, inventory_set_list),
        method(ObjectRef, inventory_get_list),
+       method(ObjectRef, get_wielded_itemstring),
+       method(ObjectRef, get_wielded_item),
+       method(ObjectRef, get_look_dir),
+       method(ObjectRef, get_look_pitch),
+       method(ObjectRef, get_look_yaw),
        {0,0}
 };
 
@@ -2148,18 +2441,69 @@ class EnvRef
                return 1;
        }
 
-       // EnvRef:add_luaentity(pos, entityname)
+       // EnvRef:get_node_or_nil(pos)
        // pos = {x=num, y=num, z=num}
-       static int l_add_luaentity(lua_State *L)
+       static int l_get_node_or_nil(lua_State *L)
        {
-               //infostream<<"EnvRef::l_add_luaentity()"<<std::endl;
+               //infostream<<"EnvRef::l_get_node()"<<std::endl;
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // pos
+               v3s16 pos = readpos(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)
+       {
+               EnvRef *o = checkobject(L, 1);
+               ServerEnvironment *env = o->m_env;
+               if(env == NULL) return 0;
+               // Do it
+               v3s16 pos = readpos(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;
+               }
+       }
+
+       // EnvRef:add_entity(pos, entityname)
+       // pos = {x=num, y=num, z=num}
+       static int l_add_entity(lua_State *L)
+       {
+               //infostream<<"EnvRef::l_add_entity()"<<std::endl;
                EnvRef *o = checkobject(L, 1);
                ServerEnvironment *env = o->m_env;
                if(env == NULL) return 0;
                // pos
                v3f pos = readFloatPos(L, 2);
                // content
-               const char *name = lua_tostring(L, 3);
+               const char *name = luaL_checkstring(L, 3);
                // Do it
                ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, "");
                env->addActiveObject(obj);
@@ -2177,7 +2521,7 @@ class EnvRef
                // pos
                v3f pos = readFloatPos(L, 2);
                // inventorystring
-               const char *inventorystring = lua_tostring(L, 3);
+               const char *inventorystring = luaL_checkstring(L, 3);
                // Do it
                ServerActiveObject *obj = new ItemSAO(env, pos, inventorystring);
                env->addActiveObject(obj);
@@ -2236,7 +2580,7 @@ class EnvRef
                ServerEnvironment *env = o->m_env;
                if(env == NULL) return 0;
                // Do it
-               const char *name = lua_tostring(L, 2);
+               const char *name = luaL_checkstring(L, 2);
                ServerRemotePlayer *player =
                                static_cast<ServerRemotePlayer*>(env->getPlayer(name));
                if(player == NULL){
@@ -2248,6 +2592,36 @@ class EnvRef
                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 = readFloatPos(L, 2);
+               float radius = luaL_checknumber(L, 3) * BS;
+               std::set<u16> ids = env->getObjectsInsideRadius(pos, radius);
+               lua_newtable(L);
+               int table = lua_gettop(L);
+               for(std::set<u16>::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;
+       }
+
        static int gc_object(lua_State *L) {
                EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1));
                delete o;
@@ -2316,12 +2690,15 @@ const luaL_reg EnvRef::methods[] = {
        method(EnvRef, add_node),
        method(EnvRef, remove_node),
        method(EnvRef, get_node),
-       method(EnvRef, add_luaentity),
+       method(EnvRef, get_node_or_nil),
+       method(EnvRef, get_node_light),
+       method(EnvRef, add_entity),
        method(EnvRef, add_item),
        method(EnvRef, add_rat),
        method(EnvRef, add_firefly),
        method(EnvRef, get_meta),
        method(EnvRef, get_player_by_name),
+       method(EnvRef, get_objects_inside_radius),
        {0,0}
 };
 
@@ -2383,6 +2760,32 @@ void scriptapi_export(lua_State *L, Server *server)
        ObjectRef::Register(L);
 }
 
+bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
+               const std::string &modname)
+{
+       ModNameStorer modnamestorer(L, modname);
+
+       if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz"
+                       "0123456789_")){
+               errorstream<<"Error loading mod \""<<modname
+                               <<"\": modname does not follow naming conventions: "
+                               <<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
+               return false;
+       }
+       
+       bool success = false;
+
+       try{
+               success = script_load(L, scriptpath.c_str());
+       }
+       catch(LuaError &e){
+               errorstream<<"Error loading mod \""<<modname
+                               <<"\": "<<e.what()<<std::endl;
+       }
+
+       return success;
+}
+
 void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
 {
        realitycheck(L);
@@ -2404,7 +2807,9 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
        lua_pushlightuserdata(L, env);
        lua_setfield(L, LUA_REGISTRYINDEX, "minetest_env");
 
-       /* Add ActiveBlockModifiers to environment */
+       /*
+               Add ActiveBlockModifiers to environment
+       */
 
        // Get minetest.registered_abms
        lua_getglobal(L, "minetest");
@@ -2432,6 +2837,25 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
                                        // 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<std::string> 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);
 
@@ -2442,7 +2866,7 @@ void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
                        getintfield(L, current_abm, "chance", trigger_chance);
 
                        LuaABM *abm = new LuaABM(L, id, trigger_contents,
-                                       trigger_interval, trigger_chance);
+                                       required_neighbors, trigger_interval, trigger_chance);
                        
                        env->addActiveBlockModifier(abm);
 
@@ -2463,7 +2887,7 @@ static void dump2(lua_State *L, const char *name)
        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\n", lua_tostring(L, -1));
+               script_error(L, "error: %s", lua_tostring(L, -1));
 }
 #endif
 
@@ -2541,7 +2965,7 @@ bool scriptapi_on_chat_message(lua_State *L, const std::string &name,
                lua_pushstring(L, name.c_str());
                lua_pushstring(L, message.c_str());
                if(lua_pcall(L, 2, 1, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                bool ate = lua_toboolean(L, -1);
                lua_pop(L, 1);
                if(ate)
@@ -2574,7 +2998,7 @@ void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player)
                // Call function
                objectref_get_or_create(L, player);
                if(lua_pcall(L, 1, 0, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                // value removed, keep key for next iteration
        }
 }
@@ -2599,7 +3023,7 @@ bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player)
                // Call function
                objectref_get_or_create(L, player);
                if(lua_pcall(L, 1, 1, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                bool positioning_handled = lua_toboolean(L, -1);
                lua_pop(L, 1);
                if(positioning_handled)
@@ -2609,6 +3033,15 @@ bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player)
        return positioning_handled_by_some;
 }
 
+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->getEnv()->getGameDef(), PLAYER_INVENTORY_SIZE);
+}
+
 /*
        craftitem
 */
@@ -2712,7 +3145,7 @@ bool scriptapi_craftitem_on_drop(lua_State *L, const char *name,
                objectref_get_or_create(L, dropper);
                pushFloatPos(L, pos);
                if(lua_pcall(L, 3, 1, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                result = lua_toboolean(L, -1);
        }
        return result;
@@ -2736,7 +3169,7 @@ bool scriptapi_craftitem_on_place_on_ground(lua_State *L, const char *name,
                objectref_get_or_create(L, placer);
                pushFloatPos(L, pos);
                if(lua_pcall(L, 3, 1, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                result = lua_toboolean(L, -1);
        }
        return result;
@@ -2760,7 +3193,7 @@ bool scriptapi_craftitem_on_use(lua_State *L, const char *name,
                objectref_get_or_create(L, user);
                pushPointedThing(L, pointed);
                if(lua_pcall(L, 3, 1, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                result = lua_toboolean(L, -1);
        }
        return result;
@@ -2790,7 +3223,7 @@ void scriptapi_environment_step(lua_State *L, float dtime)
                // Call function
                lua_pushnumber(L, dtime);
                if(lua_pcall(L, 1, 0, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                // value removed, keep key for next iteration
        }
 }
@@ -2825,7 +3258,7 @@ void scriptapi_environment_on_placenode(lua_State *L, v3s16 p, MapNode newnode,
                pushnode(L, newnode, ndef);
                objectref_get_or_create(L, placer);
                if(lua_pcall(L, 3, 0, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                // value removed, keep key for next iteration
        }
 }
@@ -2860,7 +3293,7 @@ void scriptapi_environment_on_dignode(lua_State *L, v3s16 p, MapNode oldnode,
                pushnode(L, oldnode, ndef);
                objectref_get_or_create(L, digger);
                if(lua_pcall(L, 3, 0, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                // value removed, keep key for next iteration
        }
 }
@@ -2895,7 +3328,7 @@ void scriptapi_environment_on_punchnode(lua_State *L, v3s16 p, MapNode node,
                pushnode(L, node, ndef);
                objectref_get_or_create(L, puncher);
                if(lua_pcall(L, 3, 0, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                // value removed, keep key for next iteration
        }
 }
@@ -2921,7 +3354,7 @@ void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp)
                pushpos(L, minp);
                pushpos(L, maxp);
                if(lua_pcall(L, 2, 0, 0))
-                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+                       script_error(L, "error: %s", lua_tostring(L, -1));
                // value removed, keep key for next iteration
        }
 }
@@ -3124,9 +3557,9 @@ void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime)
                script_error(L, "error running function 'on_step': %s\n", lua_tostring(L, -1));
 }
 
-// Calls entity:on_punch(ObjectRef puncher)
+// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch)
 void scriptapi_luaentity_punch(lua_State *L, u16 id,
-               ServerActiveObject *puncher)
+               ServerActiveObject *puncher, float time_from_last_punch)
 {
        realitycheck(L);
        assert(lua_checkstack(L, 20));
@@ -3144,8 +3577,9 @@ void scriptapi_luaentity_punch(lua_State *L, u16 id,
        luaL_checktype(L, -1, LUA_TFUNCTION);
        lua_pushvalue(L, object); // self
        objectref_get_or_create(L, puncher); // Clicker reference
+       lua_pushnumber(L, time_from_last_punch);
        // Call with 2 arguments, 0 results
-       if(lua_pcall(L, 2, 0, 0))
+       if(lua_pcall(L, 3, 0, 0))
                script_error(L, "error running function 'on_punch': %s\n", lua_tostring(L, -1));
 }