diff --git a/builtin/game/item.lua b/builtin/game/item.lua index 498e5899f..8b79d2d71 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -736,3 +736,22 @@ core.noneitemdef_default = { -- This is used for the hand and unknown items on_drop = nil, on_use = nil, } + +-- +-- get_node implementation +-- + +local get_node_raw = core.get_node_raw +core.get_node_raw = nil + +function core.get_node(pos) + local content, param1, param2 = get_node_raw(pos.x, pos.y, pos.z) + return {name = core.get_name_from_content_id(content), param1 = param1, param2 = param2} +end + +function core.get_node_or_nil(pos) + local content, param1, param2, pos_ok = get_node_raw(pos.x, pos.y, pos.z) + return pos_ok and + {name = core.get_name_from_content_id(content), param1 = param1, param2 = param2} + or nil +end diff --git a/games/devtest/mods/benchmarks/init.lua b/games/devtest/mods/benchmarks/init.lua index 19ba044a4..1f5001c69 100644 --- a/games/devtest/mods/benchmarks/init.lua +++ b/games/devtest/mods/benchmarks/init.lua @@ -71,6 +71,22 @@ minetest.register_chatcommand("bench_content2name", { end, }) +local function get_positions_cube(ppos) + local pos_list = {} + + local i = 1 + for x=2,100 do + for y=2,100 do + for z=2,100 do + pos_list[i] = ppos:offset(x, y, z) + i = i + 1 + end + end + end + + return pos_list +end + minetest.register_chatcommand("bench_bulk_set_node", { params = "", description = "Benchmark: Bulk-set 99×99×99 stone nodes", @@ -79,25 +95,15 @@ minetest.register_chatcommand("bench_bulk_set_node", { if not player then return false, "No player." end - local pos_list = {} - local ppos = player:get_pos() - local i = 1 - for x=2,100 do - for y=2,100 do - for z=2,100 do - pos_list[i] = {x=ppos.x + x,y = ppos.y + y,z = ppos.z + z} - i = i + 1 - end - end - end + local pos_list = get_positions_cube(player:get_pos()) - minetest.chat_send_player(name, "Benchmarking minetest.bulk_set_node. Warming up ..."); + minetest.chat_send_player(name, "Benchmarking minetest.bulk_set_node. Warming up ...") -- warm up with stone to prevent having different callbacks -- due to different node topology minetest.bulk_set_node(pos_list, {name = "mapgen_stone"}) - minetest.chat_send_player(name, "Warming up finished, now benchmarking ..."); + minetest.chat_send_player(name, "Warming up finished, now benchmarking ...") local start_time = minetest.get_us_time() for i=1,#pos_list do @@ -114,4 +120,37 @@ minetest.register_chatcommand("bench_bulk_set_node", { end, }) +minetest.register_chatcommand("bench_bulk_get_node", { + params = "", + description = "Benchmark: Bulk-get 99×99×99 nodes", + func = function(name, param) + local player = minetest.get_player_by_name(name) + if not player then + return false, "No player." + end + local pos_list = get_positions_cube(player:get_pos()) + local function bench() + local start_time = minetest.get_us_time() + for i=1,#pos_list do + local n = minetest.get_node(pos_list[i]) + -- Make sure the name lookup is never optimized away. + -- Table allocation might still be omitted. But only accessing + -- the name of a node is a common pattern anyways. + if n.name == "benchmarks:nonexistent_node" then + error("should never happen") + end + end + return minetest.get_us_time() - start_time + end + minetest.chat_send_player(name, "Benchmarking minetest.get_node. Warming up ...") + bench() + + minetest.chat_send_player(name, "Warming up finished, now benchmarking ...") + local result_us = bench() + + local msg = string.format("Benchmark results: minetest.get_node loop 1: %.2f ms", + result_us / 1000) + return true, msg + end, +}) diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index c6a427e48..3b9be8314 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -328,39 +328,26 @@ int ModApiEnv::l_swap_node(lua_State *L) return 1; } -// get_node(pos) -// pos = {x=num, y=num, z=num} -int ModApiEnv::l_get_node(lua_State *L) +// get_node_raw(x, y, z) -> content, param1, param2, pos_ok +int ModApiEnv::l_get_node_raw(lua_State *L) { GET_ENV_PTR; // pos - v3s16 pos = read_v3s16(L, 1); - // Do it - MapNode n = env->getMap().getNode(pos); - // Return node - pushnode(L, n); - return 1; -} - -// get_node_or_nil(pos) -// pos = {x=num, y=num, z=num} -int ModApiEnv::l_get_node_or_nil(lua_State *L) -{ - GET_ENV_PTR; - - // pos - v3s16 pos = read_v3s16(L, 1); + // mirrors implementation of read_v3s16 (with the exact same rounding) + double x = lua_tonumber(L, 1); + double y = lua_tonumber(L, 2); + double z = lua_tonumber(L, 3); + v3s16 pos = doubleToInt(v3d(x, y, z), 1.0); // Do it bool pos_ok; MapNode n = env->getMap().getNode(pos, &pos_ok); - if (pos_ok) { - // Return node - pushnode(L, n); - } else { - lua_pushnil(L); - } - return 1; + // Return node and pos_ok + lua_pushinteger(L, n.getContent()); + lua_pushinteger(L, n.getParam1()); + lua_pushinteger(L, n.getParam2()); + lua_pushboolean(L, pos_ok); + return 4; } // get_node_light(pos, timeofday) @@ -1483,8 +1470,7 @@ void ModApiEnv::Initialize(lua_State *L, int top) API_FCT(swap_node); API_FCT(add_item); API_FCT(remove_node); - API_FCT(get_node); - API_FCT(get_node_or_nil); + API_FCT(get_node_raw); API_FCT(get_node_light); API_FCT(get_natural_light); API_FCT(place_node); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 327cf9f75..ad4ac2d73 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -74,13 +74,10 @@ private: // pos = {x=num, y=num, z=num} static int l_swap_node(lua_State *L); - // get_node(pos) - // pos = {x=num, y=num, z=num} - static int l_get_node(lua_State *L); - - // get_node_or_nil(pos) - // pos = {x=num, y=num, z=num} - static int l_get_node_or_nil(lua_State *L); + // get_node_raw(x, y, z) -> content, param1, param2, pos_ok + // Used to implement get_node and get_node_or_nil in lua. + // This is still faster than doing it from C++ even with optimized pushnode. + static int l_get_node_raw(lua_State *L); // get_node_light(pos, timeofday) // pos = {x=num, y=num, z=num} @@ -245,7 +242,7 @@ public: /* * Duplicates of certain env APIs that operate not on the global - * map but on a VoxelManipulator. This is for emerge scripting. + * map but on a VoxelManipulator. This is for emerge scripting. */ class ModApiEnvVM : public ModApiEnvBase { private: