diff --git a/worldeditadditions/init.lua b/worldeditadditions/init.lua index 7b99785..5062784 100644 --- a/worldeditadditions/init.lua +++ b/worldeditadditions/init.lua @@ -6,15 +6,19 @@ -- @author Starbeamrainbowlabs worldeditadditions = {} +worldeditadditions.modpath = minetest.get_modpath("worldeditadditions") +dofile(worldeditadditions.modpath.."/utils/strings.lua") +dofile(worldeditadditions.modpath.."/utils/numbers.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/utils.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/lib/floodfill.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/lib/overlay.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/lib/ellipsoid.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/lib/torus.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/lib/maze2d.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/lib/maze3d.lua") +dofile(worldeditadditions.modpath.."/utils.lua") +dofile(worldeditadditions.modpath.."/lib/floodfill.lua") +dofile(worldeditadditions.modpath.."/lib/overlay.lua") +dofile(worldeditadditions.modpath.."/lib/ellipsoid.lua") +dofile(worldeditadditions.modpath.."/lib/torus.lua") +dofile(worldeditadditions.modpath.."/lib/walls.lua") +dofile(worldeditadditions.modpath.."/lib/maze2d.lua") +dofile(worldeditadditions.modpath.."/lib/maze3d.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/lib/walls.lua") +dofile(worldeditadditions.modpath.."/lib/count.lua") -dofile(minetest.get_modpath("worldeditadditions") .. "/lib/bonemeal.lua") +dofile(worldeditadditions.modpath.."/lib/bonemeal.lua") diff --git a/worldeditadditions/lib/count.lua b/worldeditadditions/lib/count.lua new file mode 100644 index 0000000..b5f1d1e --- /dev/null +++ b/worldeditadditions/lib/count.lua @@ -0,0 +1,48 @@ +--- Counts the nodes in a given area. +-- @module worldeditadditions.count + +-- ██████ ██████ ██ ██ ███ ██ ████████ +-- ██ ██ ██ ██ ██ ████ ██ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██████ ██████ ██████ ██ ████ ██ +function worldeditadditions.count(pos1, pos2) + pos1, pos2 = worldedit.sort_pos(pos1, pos2) + -- pos2 will always have the highest co-ordinates now + + -- Fetch the nodes in the specified area + local manip, area = worldedit.manip_helpers.init(pos1, pos2) + local data = manip:get_data() + + -- z y x is the preferred loop order (because CPU cache I'd guess, since then we're iterating linearly through the data array) + local counts = {} + for z = pos2.z, pos1.z, -1 do + for y = pos2.y, pos1.y, -1 do + for x = pos2.x, pos1.x, -1 do + local next = data[area:index(x, y, z)] + if not counts[next] then + counts[next] = 0 + end + counts[next] = counts[next] + 1 + end + end + end + + local total = worldedit.volume(pos1, pos2) + + local results = {} + for node_name, count in pairs(counts) do + table.insert(results, { + count, + tostring(worldeditadditions.round((count / total) * 100, 2)).."%", + minetest.get_name_from_content_id(node_name) + }) + end + table.sort(results, function(a, b) return a[1] < b[1] end) + + -- Save the modified nodes back to disk & return + -- No need to save - this function doesn't actually change anything + -- worldedit.manip_helpers.finish(manip, data) + + return true, results, total +end diff --git a/worldeditadditions_commands/utils/numbers.lua b/worldeditadditions/utils/numbers.lua similarity index 61% rename from worldeditadditions_commands/utils/numbers.lua rename to worldeditadditions/utils/numbers.lua index 75c26bc..c54bf54 100644 --- a/worldeditadditions_commands/utils/numbers.lua +++ b/worldeditadditions/utils/numbers.lua @@ -1,7 +1,5 @@ -local we_c = worldeditadditions_commands - -- From http://lua-users.org/wiki/SimpleRound -function we_c.round(num, numDecimalPlaces) +function worldeditadditions.round(num, numDecimalPlaces) local mult = 10^(numDecimalPlaces or 0) return math.floor(num * mult + 0.5) / mult end diff --git a/worldeditadditions_commands/utils/strings.lua b/worldeditadditions/utils/strings.lua similarity index 54% rename from worldeditadditions_commands/utils/strings.lua rename to worldeditadditions/utils/strings.lua index 9a31e28..6d306eb 100644 --- a/worldeditadditions_commands/utils/strings.lua +++ b/worldeditadditions/utils/strings.lua @@ -1,9 +1,6 @@ -- Licence: GPLv2 (MPL-2.0 is compatible, so we can use this here) -- Source: https://stackoverflow.com/a/43582076/1460422 -local we_c = worldeditadditions_commands - - -- gsplit: iterate over substrings in a string separated by a pattern -- -- Parameters: @@ -18,7 +15,7 @@ local we_c = worldeditadditions_commands -- for substr in gsplit(text, pattern, plain) do -- doSomething(substr) -- end -function we_c.gsplit(text, pattern, plain) +function worldeditadditions.gsplit(text, pattern, plain) local splitStart, length = 1, #text return function () if splitStart then @@ -54,10 +51,49 @@ end -- string, not a Lua pattern -- -- Returns: table (a sequence table containing the substrings) -function we_c.split(text, pattern, plain) +function worldeditadditions.split(text, pattern, plain) local ret = {} - for match in we_c.gsplit(text, pattern, plain) do + for match in worldeditadditions.gsplit(text, pattern, plain) do table.insert(ret, match) end return ret end + + +--- Pads str to length len with char from right +-- @source https://snipplr.com/view/13092/strlpad--pad-string-to-the-left +function worldeditadditions.str_padend(str, len, char) + if char == nil then char = ' ' end + return str .. string.rep(char, len - #str) +end +--- Pads str to length len with char from left +-- Adapted from the above +function worldeditadditions.str_padstart(str, len, char) + if char == nil then char = ' ' end + return string.rep(char, len - #str) .. str +end + +function worldeditadditions.make_ascii_table(data) + local extra_padding = 2 + local result = {} + local max_lengths = {} + for y = 1, #data, 1 do + for x = 1, #data[y], 1 do + if not max_lengths[x] then + max_lengths[x] = 0 + end + max_lengths[x] = math.max(max_lengths[x], #tostring(data[y][x]) + extra_padding) + end + end + + for _key, row in ipairs(data) do + local row_result = {} + for i = 1, #row, 1 do + row_result[#row_result + 1] = worldeditadditions.str_padend(tostring(row[i]), max_lengths[i], " ") + end + result[#result+1] = table.concat(row_result, "") + end + + -- TODO: Add multi-column support here + return table.concat(result, "\n") +end diff --git a/worldeditadditions_commands/commands/bonemeal.lua b/worldeditadditions_commands/commands/bonemeal.lua index 3d57ea2..9fe696e 100644 --- a/worldeditadditions_commands/commands/bonemeal.lua +++ b/worldeditadditions_commands/commands/bonemeal.lua @@ -15,7 +15,7 @@ worldedit.register_command("bonemeal", { params_text = "1" end - local parts = we_c.split(params_text, "%s+", false) + local parts = worldeditadditions.split(params_text, "%s+", false) local strength = 1 local chance = 1 @@ -52,7 +52,7 @@ worldedit.register_command("bonemeal", { -- nodes_bonemealed is an error message here because success == false return success, nodes_bonemealed end - local percentage = we_c.round((nodes_bonemealed / candidates)*100, 2) + local percentage = worldeditadditions.round((nodes_bonemealed / candidates)*100, 2) local time_taken = os.clock() - start_time minetest.log("action", name .. " used //bonemeal at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos2[name])..", bonemealing " .. nodes_bonemealed.." nodes (out of "..candidates.." nodes) at strength "..strength.." in "..time_taken.."s") diff --git a/worldeditadditions_commands/commands/count.lua b/worldeditadditions_commands/commands/count.lua new file mode 100644 index 0000000..7f18149 --- /dev/null +++ b/worldeditadditions_commands/commands/count.lua @@ -0,0 +1,35 @@ +local we_c = worldeditadditions_commands + +-- ██████ ██████ ██ ██ ███ ██ ████████ +-- ██ ██ ██ ██ ██ ████ ██ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██ ██ ██ ██ ██ ██ ██ ██ ██ +-- ██████ ██████ ██████ ██ ████ ██ +worldedit.register_command("count", { + params = "", + description = "Counts all the nodes in the defined region.", + privs = { worldedit = true }, + require_pos = 2, + parse = function(params_text) + return true + end, + nodes_needed = function(name) + -- We don't actually modify anything, but without returning a + -- number here safe_region doesn't work + return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name]) + end, + func = function(name) + local start_time = os.clock() + + local success, counts, total = worldeditadditions.count(worldedit.pos1[name], worldedit.pos2[name], target_node) + local result = worldeditadditions.make_ascii_table(counts).."\n".. + string.rep("=", 6 + #tostring(total) + 6).."\n".. + "Total "..total.." nodes\n" + + local time_taken = os.clock() - start_time + + + minetest.log("action", name.." used //count at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos1[name])..", counting "..total.." nodes in "..time_taken.."s") + return true, result + end +}) diff --git a/worldeditadditions_commands/commands/maze.lua b/worldeditadditions_commands/commands/maze.lua index ecb4721..e2b3a4c 100644 --- a/worldeditadditions_commands/commands/maze.lua +++ b/worldeditadditions_commands/commands/maze.lua @@ -11,7 +11,7 @@ local function parse_params_maze(params_text, is_3d) return false, "No arguments specified" end - local parts = we_c.split(params_text, "%s+", false) + local parts = worldeditadditions.split(params_text, "%s+", false) local replace_node = parts[1] local seed = os.time() diff --git a/worldeditadditions_commands/init.lua b/worldeditadditions_commands/init.lua index 986b133..d98b9f0 100644 --- a/worldeditadditions_commands/init.lua +++ b/worldeditadditions_commands/init.lua @@ -10,9 +10,6 @@ local we_c = worldeditadditions_commands we_c.modpath = minetest.get_modpath("worldeditadditions_commands") -dofile(we_c.modpath.."/utils/strings.lua") -dofile(we_c.modpath.."/utils/numbers.lua") - dofile(we_c.modpath.."/multi.lua") -- We no longer need our own implementation of safe_region thanks to @sfan5's @@ -24,9 +21,10 @@ dofile(we_c.modpath.."/commands/floodfill.lua") dofile(we_c.modpath.."/commands/overlay.lua") dofile(we_c.modpath.."/commands/ellipsoid.lua") dofile(we_c.modpath.."/commands/torus.lua") +dofile(we_c.modpath.."/commands/walls.lua") dofile(we_c.modpath.."/commands/maze.lua") -dofile(we_c.modpath.."/commands/walls.lua") +dofile(we_c.modpath.."/commands/count.lua") -- Don't registry the //bonemeal command if the bonemeal mod isn't present if minetest.get_modpath("bonemeal") then