--- Cleaner Chat Commands -- -- @topic commands local S = core.get_translator(cleaner.modname) local aux = dofile(cleaner.modpath .. "/misc_functions.lua") local function pos_list(ppos, radius) local plist = {} for x = ppos.x - radius, ppos.x + radius, 1 do for y = ppos.y - radius, ppos.y + radius, 1 do for z = ppos.z - radius, ppos.z + radius, 1 do table.insert(plist, {x=x, y=y, z=z}) end end end return plist end local param_def = { radius = {name=S("radius"), desc=S("Search radius.")}, entity = {name=S("entity"), desc=S("Entity technical name.")}, node = {name=S("node"), desc=S("Node technical name.")}, old_node = {name=S("old_node"), desc=S("Technical name of node to be replaced.")}, new_node = {name=S("new_node"), desc=S("Technical name of node to be used in place.")}, old_item = {name=S("old_item"), desc=S("Technical name of item to be replaced.")}, new_item = {name=S("new_item"), desc=S("Technical name of item to be used in place.")}, ore = {name=S("ore"), desc=S("Ore technical name.")}, action = {name=S("action"), desc=S('Action to execute. Can be one of "@1", "@2", or "@3".', "status", "setmode", "setnode")}, value = {name=S("value"), desc=S('Mode or node to be set for tool (not required for "@1" action).', "status")}, } local cmd_repo = { entity = { cmd = "remove_entities", params = {"entity"}, oparams = {radius=100}, }, rem_node = { cmd = "remove_nodes", params = {"node"}, oparams = {radius=5}, }, rep_node = { cmd = "replace_nodes", params = {"old_node", "new_node"}, oparams = {radius=5}, }, find_node = { cmd = "find_unknown_nodes", oparams = {radius=100}, }, near_node = { cmd = "find_nearby_nodes", oparams = {radius=5}, }, item = { cmd = "replace_items", params = {"old_item", "new_item"}, }, ore = { cmd = "remove_ores", params = {"ore"}, }, tool = { cmd = "ctool", params = {"action", "value"}, }, param = { missing = S("Missing parameter."), excess = S("Too many parameters."), mal_radius = S("Radius must be a number."), }, } for k, def in pairs(cmd_repo) do if k ~= "param" then local cmd_help = { param_string = "", usage_string = "/" .. def.cmd, } if def.params or def.oparams then if def.params then local params = {} for _, p in ipairs(def.params) do -- translate table.insert(params, S(p)) end cmd_help.param_string = "<" .. table.concat(params, "> <") .. ">" end end if def.oparams then for k, v in pairs(def.oparams) do local op = k if type(op) == "number" then op = v end cmd_help.param_string = cmd_help.param_string .. " [" .. S(op) .. "]" end end if cmd_help.param_string ~= "" then cmd_help.usage_string = cmd_help.usage_string .. " " .. cmd_help.param_string end cmd_repo[k].help = cmd_help end end local function get_cmd_def(cmd) for k, v in pairs(cmd_repo) do if v.cmd == cmd then return v end end end local function format_usage(cmd) local def = get_cmd_def(cmd) if def then return S("Usage:") .. "\n " .. def.help.usage_string end end local function format_params(cmd) local def = get_cmd_def(cmd) local param_count local all_params = {} if def.params then for k, v in ipairs(def.params) do table.insert(all_params, p) end end if def.oparams then for k, v in pairs(def.oparams) do end end local retval = "" local p_count = 0 if def.params then for _, p in ipairs(def.params) do if p_count == 0 then retval = retval .. S("Params:") end retval = retval .. "\n " .. S(p) .. ": " .. param_def[p].desc p_count = p_count + 1 end end if def.oparams then for k, v in pairs(def.oparams) do if p_count == 0 then retval = retval .. S("Params:") end local p = k local dvalue = v if type(p) == "number" then p = v dvalue = nil end retval = retval .. "\n " .. S(p) .. ": " .. param_def[p].desc if dvalue then retval = retval .. " (" .. S("default: @1", dvalue) .. ")" end p_count = p_count + 1 end end return retval end local function format_help(cmd) return format_usage(cmd) .. "\n\n" .. format_params(cmd) end local function check_radius(radius, pname) local is_admin = core.check_player_privs(pname, {server=true}) if not is_admin and radius > 10 then radius = 10 return radius, S("You do not have permission to set radius that high. Reduced to @1.", radius) end if radius > 100 then radius = 100 return radius, S("Radius is too high. Reduced to @1.", radius) end return radius end --- Removes nearby entities. -- -- @chatcmd remove_entities -- @param entity Entity technical name. -- @tparam[opt] int radius Search radius (default: 100). -- @priv server -- @usage -- # remove all mobs:horse entities within a radius of 10 nodes -- /remove_entities mobs:horse 10 core.register_chatcommand(cmd_repo.entity.cmd, { privs = {server=true}, description = S("Remove an entity from game.") .. "\n\n" .. format_params(cmd_repo.entity.cmd), params = cmd_repo.entity.help.param_string, func = function(name, param) local entity local radius = cmd_repo.entity.oparams.radius if param:find(" ") then entity = param:split(" ") radius = tonumber(entity[2]) entity = entity[1] else entity = param end local err if not entity or entity:trim() == "" then err = cmd_repo.param.missing elseif not radius then err = cmd_repo.param.mal_radius end local radius, msg = check_radius(radius, name) if msg then core.chat_send_player(name, msg) end if err then return false, err .. "\n\n" .. format_help(cmd_repo.entity.cmd) end local player = core.get_player_by_name(name) local total_removed = 0 for _, object in ipairs(core.get_objects_inside_radius(player:get_pos(), radius)) do local lent = object:get_luaentity() if lent then if lent.name == entity then object:remove() total_removed = total_removed + 1 end else if object:get_properties().infotext == entity then object:remove() total_removed = total_removed + 1 end end end return true, S("Removed @1 entities.", total_removed) end, }) --- Removes nearby nodes. -- -- @chatcmd remove_nodes -- @param node Node technical name. -- @tparam[opt] int radius Search radius (default: 5). -- @priv server -- @usage -- # remove all default:dirt nodes within a radius of 10 -- /remove_nodes default:dirt 10 core.register_chatcommand(cmd_repo.rem_node.cmd, { privs = {server=true}, description = S("Remove a node from game.") .. "\n\n" .. format_params(cmd_repo.rem_node.cmd), params = cmd_repo.rem_node.help.param_string, func = function(name, param) local nname local radius = cmd_repo.rem_node.oparams.radius if param:find(" ") then nname = param:split(" ") radius = tonumber(nname[2]) nname = nname[1] else nname = param end local err if not nname or nname:trim() == "" then err = cmd_repo.param.missing elseif not radius then err = cmd_repo.param.mal_radius end local radius, msg = check_radius(radius, name) if msg then core.chat_send_player(name, msg) end if err then return false, err .. "\n\n" .. format_help(cmd_repo.rem_node.cmd) end local ppos = core.get_player_by_name(name):get_pos() local total_removed = 0 for _, npos in ipairs(pos_list(ppos, radius)) do local node = core.get_node_or_nil(npos) if node and node.name == nname then core.remove_node(npos) total_removed = total_removed + 1 end end return true, S("Removed @1 nodes.", total_removed) end, }) --- Replaces an item. -- -- @chatcmd replace_items -- @param old_item Technical name of item to replace. -- @param new_item Technical name of item to be used in place. -- @priv server -- @usage -- # replace default:sword_wood with default:sword_mese -- /replace_items default:sword_wood default:sword_mese core.register_chatcommand(cmd_repo.item.cmd, { privs = {server=true}, description = S("Replace an item in game.") .. "\n\n" .. format_params(cmd_repo.item.cmd), params = cmd_repo.item.help.param_string, func = function(name, param) if not param:find(" ") then return false, cmd_repo.param.missing .. "\n\n" .. format_help(cmd_repo.item.cmd) end local src = param:split(" ") local tgt = src[2] src = src[1] local retval, msg = cleaner.replace_item(src, tgt, true) if not retval then return false, msg end return true, S("Success!") end, }) --- Replaces nearby nodes. -- -- @chatcmd replace_nodes -- @param old_node Technical name of node to replace. -- @param new_node Technical name of node to be used in place. -- @tparam[opt] int radius Search radius (default: 5). -- @priv server -- @usage -- # replace all default:dirt nodes with default:cobble within a radius of 10 -- /replace_nodes default:dirt default:cobble 10 core.register_chatcommand(cmd_repo.rep_node.cmd, { privs = {server=true}, description = S("Replace a node in game.") .. "\n\n" .. format_params(cmd_repo.rep_node.cmd), params = cmd_repo.rep_node.help.param_string, func = function(name, param) local help = format_help(cmd_repo.rep_node.cmd) if not param:find(" ") then return false, cmd_repo.param.missing .. "\n\n" .. help end local radius = cmd_repo.rep_node.oparams.radius local params = param:split(" ") local src = params[1] local tgt = tostring(params[2]) if #params > 2 then radius = tonumber(params[3]) end if not radius then return false, cmd_repo.param.mal_radius .. "\n\n" .. help end local radius, msg = check_radius(radius, name) if msg then core.chat_send_player(name, msg) end if not core.registered_nodes[tgt] then return false, S('Cannot use unknown node "@1" as replacement.', tgt) end local total_replaced = 0 local ppos = core.get_player_by_name(name):get_pos() for _, npos in ipairs(pos_list(ppos, radius)) do local node = core.get_node_or_nil(npos) if node and node.name == src then if core.swap_node(npos, {name=tgt}) then total_replaced = total_replaced + 1 else cleaner.log("error", "could not replace node at " .. core.pos_to_string(npos, 0)) end end end return true, S("Replaced @1 nodes.", total_replaced) end, }) --- Checks for nearby unknown nodes. -- -- @chatcmd find_unknown_nodes -- @tparam[opt] int radius Search radius (default: 100). -- @priv server -- @usage -- # print names of all unknown nodes within radius of 10 -- /find_unknown_nodes 10 core.register_chatcommand(cmd_repo.find_node.cmd, { privs = {server=true}, description = S("Find names of unknown nodes.") .. "\n\n" .. format_params(cmd_repo.find_node.cmd), params = cmd_repo.find_node.help.param_string, func = function(name, param) local help = format_help(cmd_repo.find_node.cmd) if param:find(" ") then return false, cmd_repo.param.excess .. "\n\n" .. help end local radius = cmd_repo.find_node.oparams.radius if param and param:trim() ~= "" then radius = tonumber(param) end if not radius then return false, cmd_repo.param.mal_radius .. "\n\n" .. help end local radius, msg = check_radius(radius, name) if msg then core.chat_send_player(name, msg) end local ppos = core.get_player_by_name(name):get_pos() local checked_nodes = {} local unknown_nodes = {} for _, npos in ipairs(pos_list(ppos, radius)) do local node = core.get_node_or_nil(npos) if node and not checked_nodes[node.name] then if not core.registered_nodes[node.name] then table.insert(unknown_nodes, node.name) end checked_nodes[node.name] = true end end local node_count = #unknown_nodes if node_count > 0 then msg = S("Found unknown nodes: @1", node_count) .. "\n " .. table.concat(unknown_nodes, ", ") else msg = S("No unknown nodes found.") end return true, msg end, }) --- Finds names of nearby nodes. -- -- @chatcmd find_nearby_nodes -- @tparam[opt] int radius Search radius (default: 5). -- @priv server -- @usage -- # print names of all node types found within radius of 10 -- /find_nearby_nodes 10 core.register_chatcommand(cmd_repo.near_node.cmd, { privs = {server=true}, description = S("Find names of nearby nodes.") .. "\n\n" .. format_params(cmd_repo.near_node.cmd), params = cmd_repo.near_node.help.param_string, func = function(name, param) local help = format_help(cmd_repo.near_node.cmd) if param:find(" ") then return false, cmd_repo.param.excess .. "\n\n" .. help end local radius = cmd_repo.near_node.oparams.radius if param and param:trim() ~= "" then radius = tonumber(param) end if not radius then return false, cmd_repo.param.mal_radius .. "\n\n" .. help end local radius, msg = check_radius(radius, name) if msg then core.chat_send_player(name, msg) end local ppos = core.get_player_by_name(name):get_pos() local node_names = {} for _, npos in ipairs(pos_list(ppos, radius)) do local node = core.get_node_or_nil(npos) if node and not node_names[node.name] then node_names[node.name] = true end end local found_nodes = {} for k, _ in pairs(node_names) do table.insert(found_nodes, k) end local msg local node_count = #found_nodes if node_count > 0 then msg = S("Nearby nodes: @1", node_count) .. "\n " .. table.concat(found_nodes, ", ") else msg = S("No nearby nodes found.") end return true, msg end, }) --- Unsafe Commands. -- -- Enabled with [cleaner.unsafe](settings.html#cleaner.unsafe) setting. -- -- @section unsafe if cleaner.unsafe then --- Registers an ore to be removed. -- -- @chatcmd remove_ores -- @param ore Ore technical name. -- @priv server -- @note This action is reverted after server restart. To make changes permanent, -- use the [cleaner.json](config.html#cleaner.json) config. -- @usage -- # remove all registered ores that add default:stone_with_iron to world -- /remove_ores default:stone_with_iron core.register_chatcommand(cmd_repo.ore.cmd, { privs = {server=true}, description = S("Remove an ore from game.") .. "\n\n" .. format_params(cmd_repo.ore.cmd), params = cmd_repo.ore.help.param_string, func = function(name, param) local err if not param or param:trim() == "" then err = cmd_repo.param.missing elseif param:find(" ") then err = cmd_repo.param.excess end if err then return false, err .. "\n\n" .. format_help(cmd_repo.ore.cmd) end local success = false local msg local registered, total_removed = cleaner.remove_ore(param) if not registered then msg = S('Ore "@1" not found, not unregistering.', param) else msg = S("Unregistered @1 ores (this will be undone after server restart).", total_removed) success = true end return success, msg end }) end --- @section end --- Manages settings for wielded [cleaner tool](tools.html). -- --

Required Privileges:

-- -- - server -- -- @chatcmd ctool -- @param action Action to execute. Can be "status", "setmode", or "setnode". -- @param value Mode or node to be set for tool (not required for "status" action). -- @usage -- # while cleaner:pencil is wielded, configure to place default:dirt node when used -- /ctool setmode write -- /ctool setnode default:dirt core.register_chatcommand(cmd_repo.tool.cmd, { privs = {server=true}, description = S("Manage settings for wielded cleaner tool.") .. "\n\n" .. format_params(cmd_repo.tool.cmd), params = cmd_repo.tool.help.param_string, func = function(name, param) local action, value = param local idx = param:find(" ") if idx then param = string.split(param, " ") action = param[1] value = param[2] end local help = format_help(cmd_repo.tool.cmd) local player = core.get_player_by_name(name) local stack = player:get_wielded_item() local iname = aux.tool:format_name(stack) local imeta = stack:get_meta() if iname ~= "cleaner:pencil" then return false, S("Unrecognized wielded item: @1", iname) .. "\n\n" .. help end if action == "status" then core.chat_send_player(name, iname .. ": " .. S("mode") .. "=" .. imeta:get_string("mode") .. ", " .. S("node") .. "=" .. imeta:get_string("node")) return true end if not action or not value then return false, S("Missing parameter.") .. "\n\n" .. help end if action == "setmode" then stack = aux.tool:set_mode(stack, value, name) elseif action == "setnode" then stack = aux.tool:set_node(stack, value, name) else return false, S("Unrecognized action: @1", action) .. "\n\n" .. help end return player:set_wielded_item(stack) end, })