Improve LuaEntity velocity/acceleration handling (by kahrl); implement staticdata...
authorPerttu Ahola <celeron55@gmail.com>
Mon, 21 Nov 2011 09:15:15 +0000 (11:15 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 29 Nov 2011 17:13:49 +0000 (19:13 +0200)
data/mods/default/init.lua
src/content_cao.cpp
src/content_cao.h
src/content_sao.cpp
src/content_sao.h
src/scriptapi.cpp
src/scriptapi.h
src/server.cpp

index 949816c9edc16a2f11c0313241faf5ee7794e634..a8a1d08f4c895160412c20d4a8856cf6ebac7544 100644 (file)
@@ -101,6 +101,7 @@ end
 -- - add_node(pos, node)
 -- - remove_node(pos)
 -- - get_node(pos)
+-- - add_luaentity(pos, name)
 --
 -- ObjectRef is basically ServerActiveObject.
 -- ObjectRef methods:
@@ -114,6 +115,12 @@ end
 -- - Functions receive a "luaentity" as self:
 --   - It has the member .object, which is an ObjectRef pointing to the object
 --   - The original prototype stuff is visible directly via a metatable
+-- - Callbacks:
+--   - on_activate(self, staticdata)
+--   - on_step(self, dtime)
+--   - on_punch(self, hitter)
+--   - on_rightclick(self, clicker)
+--   - get_staticdata(self): return string
 --
 -- MapNode representation:
 -- {name="name", param1=num, param2=num}
@@ -654,11 +661,20 @@ local TNT = {
        --textures = {"mese.png^[forcesingle"},
        -- Initial value for our timer
        timer = 0,
+       -- Number of punches required to defuse
+       health = 3,
        -- List names of state variables, for serializing object state
        -- (NOTE: not implemented and implementation will not be like this)
        -- state_variables = {"timer"},
 }
 
+-- Called when a TNT object is created
+function TNT:on_activate(staticdata)
+       print("TNT:on_activate()")
+       self.object:setvelocity({x=0, y=1, z=0})
+       self.object:setacceleration({x=0, y=-5, z=0})
+end
+
 -- Called periodically
 function TNT:on_step(dtime)
        --print("TNT:on_step()")
@@ -667,15 +683,18 @@ end
 -- Called when object is punched
 function TNT:on_punch(hitter)
        print("TNT:on_punch()")
-       self.object:remove()
-       hitter:add_to_inventory("CraftItem testobject1 1")
+       self.health = self.health - 1
+       if self.health <= 0 then
+               self.object:remove()
+               hitter:add_to_inventory("NodeItem TNT 1")
+       end
 end
 
 -- Called when object is right-clicked
 function TNT:on_rightclick(clicker)
-       pos = self.object:getpos()
-       pos = {x=pos.x, y=pos.y+0.1, z=pos.z}
-       self.object:moveto(pos, false)
+       --pos = self.object:getpos()
+       --pos = {x=pos.x, y=pos.y+0.1, z=pos.z}
+       --self.object:moveto(pos, false)
 end
 
 print("TNT dump: "..dump(TNT))
@@ -694,16 +713,13 @@ function register_falling_node(nodename, texture)
                visual = "cube",
                textures = {texture,texture,texture,texture,texture,texture},
                -- State
-               fallspeed = 0,
                -- Methods
                on_step = function(self, dtime)
-                       -- Apply gravity manually
-                       self.fallspeed = self.fallspeed + dtime * 5
-                       fp = self.object:getpos()
-                       fp.y = fp.y - self.fallspeed * dtime
-                       self.object:moveto(fp, true)
+                       -- Set gravity
+                       self.object:setacceleration({x=0, y=-10, z=0})
                        -- Turn to actual sand when collides to ground or just move
-                       bcp = {x=fp.x, y=fp.y-0.5, z=fp.z} -- Position of bottom center point
+                       pos = self.object:getpos()
+                       bcp = {x=pos.x, y=pos.y-0.5, z=pos.z} -- Position of bottom center point
                        bcn = minetest.env:get_node(bcp)
                        if bcn.name ~= "air" then
                                -- Turn to a sand node
@@ -772,6 +788,16 @@ function on_dignode(p, node)
 end
 minetest.register_on_dignode(on_dignode)
 
+function on_punchnode(p, node)
+       print("on_punchnode")
+       if node.name == "TNT" then
+               minetest.env:remove_node(p)
+               minetest.env:add_luaentity(p, "TNT")
+               nodeupdate(p)
+       end
+end
+minetest.register_on_punchnode(on_punchnode)
+
 --
 -- Done, print some random stuff
 --
index ca9e22811d320598963b5335f2e8fb683a06f486..f5ef3fb0759051dc2ad69b787f76c29df79588bb 100644 (file)
@@ -1278,6 +1278,8 @@ LuaEntityCAO::LuaEntityCAO(IGameDef *gamedef):
        m_meshnode(NULL),
        m_spritenode(NULL),
        m_position(v3f(0,10*BS,0)),
+       m_velocity(v3f(0,0,0)),
+       m_acceleration(v3f(0,0,0)),
        m_yaw(0),
        m_prop(new LuaEntityProperties)
 {
@@ -1455,6 +1457,9 @@ void LuaEntityCAO::updateNodePos()
 
 void LuaEntityCAO::step(float dtime, ClientEnvironment *env)
 {
+       m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
+       m_velocity += dtime * m_acceleration;
+       pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
        pos_translator.translate(dtime);
        updateNodePos();
 }
@@ -1471,6 +1476,10 @@ void LuaEntityCAO::processMessage(const std::string &data)
                bool do_interpolate = readU8(is);
                // pos
                m_position = readV3F1000(is);
+               // velocity
+               m_velocity = readV3F1000(is);
+               // acceleration
+               m_acceleration = readV3F1000(is);
                // yaw
                m_yaw = readF1000(is);
                // is_end_position (for interpolation)
index 3a3fbbcda071a4c259bf48bcc1149085950ab67e..3f6b9d877c293e19c2b81b81436e5e8938559707 100644 (file)
@@ -428,6 +428,8 @@ private:
        scene::IMeshSceneNode *m_meshnode;
        scene::MyBillboardSceneNode *m_spritenode;
        v3f m_position;
+       v3f m_velocity;
+       v3f m_acceleration;
        float m_yaw;
        struct LuaEntityProperties *m_prop;
        SmoothTranslator pos_translator;
index dc112275fe6c22b78c70c3099c9ab461139ace1b..986e3f15f657058309323960b8ae19fc6fd7fbe5 100644 (file)
@@ -1550,6 +1550,8 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
        m_init_state(state),
        m_registered(false),
        m_prop(new LuaEntityProperties),
+       m_velocity(0,0,0),
+       m_acceleration(0,0,0),
        m_yaw(0),
        m_last_sent_yaw(0),
        m_last_sent_position(0,0,0),
@@ -1610,6 +1612,9 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
 {
        m_last_sent_position_timer += dtime;
        
+       m_base_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
+       m_velocity += dtime * m_acceleration;
+
        if(m_registered){
                lua_State *L = m_env->getLua();
                scriptapi_luaentity_step(L, m_id, dtime);
@@ -1618,6 +1623,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
        if(send_recommended == false)
                return;
        
+       // TODO: force send when velocity/acceleration changes enough
        float minchange = 0.2*BS;
        if(m_last_sent_position_timer > 1.0){
                minchange = 0.01*BS;
@@ -1659,7 +1665,7 @@ std::string LuaEntitySAO::getStaticData()
        // state
        if(m_registered){
                lua_State *L = m_env->getLua();
-               std::string state = scriptapi_luaentity_get_state(L, m_id);
+               std::string state = scriptapi_luaentity_get_staticdata(L, m_id);
                os<<serializeLongString(state);
        } else {
                os<<serializeLongString(m_init_state);
@@ -1709,6 +1715,16 @@ float LuaEntitySAO::getMinimumSavedMovement()
        return 0.1 * BS;
 }
 
+void LuaEntitySAO::setVelocity(v3f velocity)
+{
+       m_velocity = velocity;
+}
+
+void LuaEntitySAO::setAcceleration(v3f acceleration)
+{
+       m_acceleration = acceleration;
+}
+
 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
 {
        m_last_sent_move_precision = m_base_position.getDistanceFrom(
@@ -1716,6 +1732,8 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
        m_last_sent_position_timer = 0;
        m_last_sent_yaw = m_yaw;
        m_last_sent_position = m_base_position;
+       //m_last_sent_velocity = m_velocity;
+       //m_last_sent_acceleration = m_acceleration;
 
        float update_interval = m_env->getSendRecommendedInterval();
 
@@ -1727,6 +1745,10 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
        writeU8(os, do_interpolate);
        // pos
        writeV3F1000(os, m_base_position);
+       // velocity
+       writeV3F1000(os, m_velocity);
+       // acceleration
+       writeV3F1000(os, m_acceleration);
        // yaw
        writeF1000(os, m_yaw);
        // is_end_position (for interpolation)
index cd747496050eeeca7029a4a99bd8e9bad7e2800a..04d33647edba3eaef6fcfff8e2b50c65754adf7f 100644 (file)
@@ -216,6 +216,8 @@ public:
        void setPos(v3f pos);
        void moveTo(v3f pos, bool continuous);
        float getMinimumSavedMovement();
+       void setVelocity(v3f velocity);
+       void setAcceleration(v3f acceleration);
 private:
        void sendPosition(bool do_interpolate, bool is_movement_end);
 
@@ -224,6 +226,8 @@ private:
        bool m_registered;
        struct LuaEntityProperties *m_prop;
        
+       v3f m_velocity;
+       v3f m_acceleration;
        float m_yaw;
        float m_last_sent_yaw;
        v3f m_last_sent_position;
index a18c144d3b8837e6cfc802ab2c9db4898725cc73..02db2ce024b8180aa715e35eff25742ddf00c046 100644 (file)
@@ -61,6 +61,7 @@ TODO:
        meta.set("owner", playername)
        meta.get("owner")
 - Item definition (actually, only CraftItem)
+- (not scripting) Putting items in node metadata (virtual)
 */
 
 static void stackDump(lua_State *L, std::ostream &o)
@@ -424,24 +425,21 @@ static int l_register_craft(lua_State *L)
        return 0; /* number of results */
 }
 
-// Register a global step function
-// register_globalstep(function)
-static int l_register_globalstep(lua_State *L)
+static int register_lua_callback(lua_State *L, const char *tablename)
 {
        luaL_checktype(L, 1, LUA_TFUNCTION);
-       infostream<<"register_globalstep"<<std::endl;
 
        lua_getglobal(L, "table");
        lua_getfield(L, -1, "insert");
        int table_insert = lua_gettop(L);
        // Get minetest.registered_globalsteps
        lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "registered_globalsteps");
+       lua_getfield(L, -1, tablename);
        luaL_checktype(L, -1, LUA_TTABLE);
-       int registered_globalsteps = lua_gettop(L);
+       int registered = lua_gettop(L);
        // table.insert(registered_globalsteps, func)
        lua_pushvalue(L, table_insert);
-       lua_pushvalue(L, registered_globalsteps);
+       lua_pushvalue(L, registered);
        lua_pushvalue(L, 1); // push function from argument 1
        // Call insert
        if(lua_pcall(L, 2, 0, 0))
@@ -450,54 +448,33 @@ static int l_register_globalstep(lua_State *L)
        return 0; /* number of results */
 }
 
+// Register a global step function
+// register_globalstep(function)
+static int l_register_globalstep(lua_State *L)
+{
+       infostream<<"register_globalstep"<<std::endl;
+       return register_lua_callback(L, "registered_globalsteps");
+}
+
 // register_on_placenode(function)
 static int l_register_on_placenode(lua_State *L)
 {
-       luaL_checktype(L, 1, LUA_TFUNCTION);
        infostream<<"register_on_placenode"<<std::endl;
-
-       lua_getglobal(L, "table");
-       lua_getfield(L, -1, "insert");
-       int table_insert = lua_gettop(L);
-       // Get minetest.registered_on_placenodes
-       lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "registered_on_placenodes");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       int registered_on_placenodes = lua_gettop(L);
-       // table.insert(registered_on_placenodes, func)
-       lua_pushvalue(L, table_insert);
-       lua_pushvalue(L, registered_on_placenodes);
-       lua_pushvalue(L, 1); // push function from argument 1
-       // Call insert
-       if(lua_pcall(L, 2, 0, 0))
-               script_error(L, "error: %s\n", lua_tostring(L, -1));
-
-       return 0; /* number of results */
+       return register_lua_callback(L, "registered_on_placenodes");
 }
 
 // register_on_dignode(function)
 static int l_register_on_dignode(lua_State *L)
 {
-       luaL_checktype(L, 1, LUA_TFUNCTION);
        infostream<<"register_on_dignode"<<std::endl;
+       return register_lua_callback(L, "registered_on_dignodes");
+}
 
-       lua_getglobal(L, "table");
-       lua_getfield(L, -1, "insert");
-       int table_insert = lua_gettop(L);
-       // Get minetest.registered_on_dignodes
-       lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "registered_on_dignodes");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       int registered_on_dignodes = lua_gettop(L);
-       // table.insert(registered_on_dignodes, func)
-       lua_pushvalue(L, table_insert);
-       lua_pushvalue(L, registered_on_dignodes);
-       lua_pushvalue(L, 1); // push function from argument 1
-       // Call insert
-       if(lua_pcall(L, 2, 0, 0))
-               script_error(L, "error: %s\n", lua_tostring(L, -1));
-
-       return 0; /* number of results */
+// register_on_punchnode(function)
+static int l_register_on_punchnode(lua_State *L)
+{
+       infostream<<"register_on_punchnode"<<std::endl;
+       return register_lua_callback(L, "registered_on_punchnodes");
 }
 
 static const struct luaL_Reg minetest_f [] = {
@@ -508,6 +485,7 @@ static const struct luaL_Reg minetest_f [] = {
        {"register_globalstep", l_register_globalstep},
        {"register_on_placenode", l_register_on_placenode},
        {"register_on_dignode", l_register_on_dignode},
+       {"register_on_punchnode", l_register_on_punchnode},
        {NULL, NULL}
 };
 
@@ -583,8 +561,9 @@ private:
                // content
                MapNode n = readnode(L, 3, env->getGameDef()->ndef());
                // Do it
-               env->getMap().addNodeWithEvent(pos, n);
-               return 0;
+               bool succeeded = env->getMap().addNodeWithEvent(pos, n);
+               lua_pushboolean(L, succeeded);
+               return 1;
        }
 
        // EnvRef:remove_node(pos)
@@ -598,8 +577,9 @@ private:
                // pos
                v3s16 pos = readpos(L, 2);
                // Do it
-               env->getMap().removeNodeWithEvent(pos);
-               return 0;
+               bool succeeded = env->getMap().removeNodeWithEvent(pos);
+               lua_pushboolean(L, succeeded);
+               return 1;
        }
 
        // EnvRef:get_node(pos)
@@ -810,6 +790,32 @@ private:
                return 0;
        }
 
+       // setvelocity(self, velocity)
+       static int l_setvelocity(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               LuaEntitySAO *co = getluaobject(ref);
+               if(co == NULL) return 0;
+               // pos
+               v3f pos = readFloatPos(L, 2);
+               // Do it
+               co->setVelocity(pos);
+               return 0;
+       }
+       
+       // setacceleration(self, acceleration)
+       static int l_setacceleration(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               LuaEntitySAO *co = getluaobject(ref);
+               if(co == NULL) return 0;
+               // pos
+               v3f pos = readFloatPos(L, 2);
+               // Do it
+               co->setAcceleration(pos);
+               return 0;
+       }
+       
        // add_to_inventory(self, itemstring)
        // returns: true if item was added, false otherwise
        static int l_add_to_inventory(lua_State *L)
@@ -902,6 +908,8 @@ const luaL_reg ObjectRef::methods[] = {
        method(ObjectRef, getpos),
        method(ObjectRef, setpos),
        method(ObjectRef, moveto),
+       method(ObjectRef, setvelocity),
+       method(ObjectRef, setacceleration),
        method(ObjectRef, add_to_inventory),
        {0,0}
 };
@@ -957,6 +965,9 @@ void scriptapi_export(lua_State *L, Server *server)
        lua_newtable(L);
        lua_setfield(L, -2, "registered_on_dignodes");
 
+       lua_newtable(L);
+       lua_setfield(L, -2, "registered_on_punchnodes");
+
        lua_newtable(L);
        lua_setfield(L, -2, "object_refs");
 
@@ -1159,12 +1170,45 @@ void scriptapi_environment_on_dignode(lua_State *L, v3s16 p, MapNode oldnode)
        }
 }
 
+void scriptapi_environment_on_punchnode(lua_State *L, v3s16 p, MapNode oldnode)
+{
+       realitycheck(L);
+       assert(lua_checkstack(L, 20));
+       //infostream<<"scriptapi_environment_on_punchnode"<<std::endl;
+       StackUnroller stack_unroller(L);
+
+       // 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 *ndef =
+                       server->getWritableNodeDefManager();
+       
+       // Get minetest.registered_on_punchnodes
+       lua_getglobal(L, "minetest");
+       lua_getfield(L, -1, "registered_on_punchnodes");
+       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
+               pushpos(L, p);
+               pushnode(L, oldnode, ndef);
+               if(lua_pcall(L, 2, 0, 0))
+                       script_error(L, "error: %s\n", lua_tostring(L, -1));
+               // value removed, keep key for next iteration
+       }
+}
+
 /*
        luaentity
 */
 
 bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name,
-               const char *init_state)
+               const std::string &staticdata)
 {
        realitycheck(L);
        assert(lua_checkstack(L, 20));
@@ -1172,8 +1216,6 @@ bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name,
                        <<name<<"\""<<std::endl;
        StackUnroller stack_unroller(L);
        
-       // Create object as a dummy string (TODO: Create properly)
-
        // Get minetest.registered_entities[name]
        lua_getglobal(L, "minetest");
        lua_getfield(L, -1, "registered_entities");
@@ -1213,17 +1255,19 @@ bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name,
        lua_pushvalue(L, object); // Copy object to top of stack
        lua_settable(L, -3);
        
-       // This callback doesn't really make sense
-       /*// Get on_activate function
+       // Get on_activate function
        lua_pushvalue(L, object);
        lua_getfield(L, -1, "on_activate");
-       luaL_checktype(L, -1, LUA_TFUNCTION);
-       lua_pushvalue(L, object); // self
-       // Call with 1 arguments, 0 results
-       if(lua_pcall(L, 1, 0, 0))
-               script_error(L, "error running function %s:on_activate: %s\n",
-                               name, lua_tostring(L, -1));*/
-
+       if(!lua_isnil(L, -1)){
+               luaL_checktype(L, -1, LUA_TFUNCTION);
+               lua_pushvalue(L, object); // self
+               lua_pushlstring(L, staticdata.c_str(), staticdata.size());
+               // Call with 2 arguments, 0 results
+               if(lua_pcall(L, 2, 0, 0))
+                       script_error(L, "error running function %s:on_activate: %s\n",
+                                       name, lua_tostring(L, -1));
+       }
+       
        return true;
 }
 
@@ -1247,13 +1291,33 @@ void scriptapi_luaentity_rm(lua_State *L, u16 id)
        lua_pop(L, 2); // pop luaentities, minetest
 }
 
-std::string scriptapi_luaentity_get_state(lua_State *L, u16 id)
+std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id)
 {
        realitycheck(L);
        assert(lua_checkstack(L, 20));
-       infostream<<"scriptapi_luaentity_get_state: id="<<id<<std::endl;
+       infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl;
+       StackUnroller stack_unroller(L);
+
+       // Get minetest.luaentities[id]
+       luaentity_get(L, id);
+       int object = lua_gettop(L);
+       
+       // Get get_staticdata function
+       lua_pushvalue(L, object);
+       lua_getfield(L, -1, "get_staticdata");
+       if(lua_isnil(L, -1))
+               return "";
+       
+       luaL_checktype(L, -1, LUA_TFUNCTION);
+       lua_pushvalue(L, object); // self
+       // Call with 1 arguments, 1 results
+       if(lua_pcall(L, 1, 1, 0))
+               script_error(L, "error running function get_staticdata: %s\n",
+                               lua_tostring(L, -1));
        
-       return "";
+       size_t len=0;
+       const char *s = lua_tolstring(L, -1, &len);
+       return std::string(s, len);
 }
 
 void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
@@ -1344,7 +1408,7 @@ void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime)
        lua_pushnumber(L, dtime); // dtime
        // Call with 2 arguments, 0 results
        if(lua_pcall(L, 2, 0, 0))
-               script_error(L, "error running function 'step': %s\n", lua_tostring(L, -1));
+               script_error(L, "error running function 'on_step': %s\n", lua_tostring(L, -1));
 }
 
 // Calls entity:on_punch(ObjectRef puncher)
index e7ff84039e8aea72d26327f2754e4ae15b0a4aff..46bc8233b2f50ad9516b337d051c38d9f5495109 100644 (file)
@@ -44,13 +44,15 @@ void scriptapi_environment_step(lua_State *L, float dtime);
 void scriptapi_environment_on_placenode(lua_State *L, v3s16 p, MapNode newnode);
 // After removing node
 void scriptapi_environment_on_dignode(lua_State *L, v3s16 p, MapNode oldnode);
+// When punching node
+void scriptapi_environment_on_punchnode(lua_State *L, v3s16 p, MapNode node);
 
 /* luaentity */
 // Returns true if succesfully added into Lua; false otherwise.
 bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name,
-               const char *init_state);
+               const std::string &staticdata);
 void scriptapi_luaentity_rm(lua_State *L, u16 id);
-std::string scriptapi_luaentity_get_state(lua_State *L, u16 id);
+std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id);
 void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
                LuaEntityProperties *prop);
 void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime);
index 4671530310198bcd64c720c179cc6f1758d903e3..ad80851dd496d69a3a01f21dc99e31c298d2f4de 100644 (file)
@@ -2501,6 +2501,32 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                NOTE: This can be used in the future to check if
                                somebody is cheating, by checking the timing.
                        */
+                       bool cannot_punch_node = false;
+
+                       MapNode n(CONTENT_IGNORE);
+
+                       try
+                       {
+                               n = m_env->getMap().getNode(p_under);
+                       }
+                       catch(InvalidPositionException &e)
+                       {
+                               infostream<<"Server: Not punching: Node not found."
+                                               <<" Adding block to emerge queue."
+                                               <<std::endl;
+                               m_emerge_queue.addBlock(peer_id,
+                                               getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
+                               cannot_punch_node = true;
+                       }
+
+                       if(cannot_punch_node)
+                               return;
+
+                       /*
+                               Run script hook
+                       */
+                       scriptapi_environment_on_punchnode(m_lua, p_under, n);
+
                } // action == 0
 
                /*