diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5e0f2f118..e6e24b90a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1150,15 +1150,18 @@ Node definition (register_node) on_destruct = func(pos), ^ Node destructor; always called before removing node ^ default: nil + after_destruct = func(pos, oldnode), + ^ Node destructor; always called after removing node + ^ default: nil after_place_node = func(pos, placer), ^ Called after constructing node when node was placed using - minetest.item_place_node + minetest.item_place_node / minetest.env:place_node ^ default: nil after_dig_node = func(pos, oldnode, oldmetadata, digger), ^ oldmetadata is in table format ^ Called after destructing node when node was dug using - minetest.node_dig + minetest.node_dig / minetest.env:dig_node ^ default: nil can_dig = function(pos,player) ^ returns true if node can be dug, or false if not diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index ea11e6d3b..c8ffb25de 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -468,6 +468,10 @@ minetest.register_node("experimental:tester_node_1", { experimental.print_to_everything("experimental:tester_node_1:on_destruct("..minetest.pos_to_string(pos)..")") end, + after_destruct = function(pos) + experimental.print_to_everything("experimental:tester_node_1:after_destruct("..minetest.pos_to_string(pos)..")") + end, + after_dig_node = function(pos, oldnode, oldmetadata, digger) experimental.print_to_everything("experimental:tester_node_1:after_dig_node("..minetest.pos_to_string(pos)..")") end, diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 2feec04f8..eaf061287 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -117,6 +117,9 @@ void ContentFeatures::reset() visual_solidness = 0; backface_culling = true; #endif + has_on_construct = false; + has_on_destruct = false; + has_after_destruct = false; /* Actual data diff --git a/src/nodedef.h b/src/nodedef.h index 681606fd8..04f375043 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -144,6 +144,11 @@ struct ContentFeatures bool backface_culling; #endif + // Server-side cached callback existence for fast skipping + bool has_on_construct; + bool has_on_destruct; + bool has_after_destruct; + /* Actual data */ diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 5091216dc..f53355f64 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -994,6 +994,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); @@ -3024,20 +3036,26 @@ private: EnvRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; - // pos + INodeDefManager *ndef = env->getGameDef()->ndef(); + // parameters v3s16 pos = read_v3s16(L, 2); - // content - MapNode n = readnode(L, 3, env->getGameDef()->ndef()); + MapNode n = readnode(L, 3, ndef); // Do it - // Call destructor MapNode n_old = env->getMap().getNodeNoEx(pos); - scriptapi_node_on_destruct(L, pos, n_old); + // Call destructor + if(ndef->get(n_old).has_on_destruct) + scriptapi_node_on_destruct(L, pos, n_old); // Replace node bool succeeded = env->getMap().addNodeWithEvent(pos, n); + if(succeeded){ + // Call post-destructor + if(ndef->get(n_old).has_after_destruct) + scriptapi_node_after_destruct(L, pos, n_old); + // Call constructor + if(ndef->get(n).has_on_construct) + scriptapi_node_on_construct(L, pos, n); + } lua_pushboolean(L, succeeded); - // Call constructor - if(succeeded) - scriptapi_node_on_construct(L, pos, n); return 1; } @@ -3053,14 +3071,22 @@ private: 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 + MapNode n_old = env->getMap().getNodeNoEx(pos); // Call destructor - MapNode n = env->getMap().getNodeNoEx(pos); - scriptapi_node_on_destruct(L, pos, n); + if(ndef->get(n_old).has_on_destruct) + scriptapi_node_on_destruct(L, pos, n_old); // Replace with air // This is slightly optimized compared to addNodeWithEvent(air) bool succeeded = env->getMap().removeNodeWithEvent(pos); + if(succeeded){ + // Call post-destructor + if(ndef->get(n_old).has_after_destruct) + scriptapi_node_after_destruct(L, pos, n_old); + } lua_pushboolean(L, succeeded); // Air doesn't require constructor return 1; @@ -5143,6 +5169,25 @@ void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node) script_error(L, "error: %s", lua_tostring(L, -1)); } +void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "after_destruct")) + return; + + // Call function + push_v3s16(L, p); + pushnode(L, node, ndef); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, const std::string &formname, const std::map &fields, diff --git a/src/scriptapi.h b/src/scriptapi.h index 7b0bffdb5..59daa3aa0 100644 --- a/src/scriptapi.h +++ b/src/scriptapi.h @@ -86,6 +86,8 @@ bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node, void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node); // Node destructor void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node); +// Node post-destructor +void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node); // Called when a metadata form returns values void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, const std::string &formname,