]> git.lizzy.rs Git - minetest.git/blobdiff - builtin/game/chatcommands.lua
Sounds: Add falling node sounds
[minetest.git] / builtin / game / chatcommands.lua
index 5d59559728c6c672e925c7cf2b0d49b12eb7c99f..cbfe2fb257bb82412a26fa0228f1116f71e0d352 100644 (file)
@@ -1,46 +1,28 @@
--- Minetest: builtin/chatcommands.lua
+-- Minetest: builtin/game/chatcommands.lua
 
 --
 -- Chat command handler
 --
 
-core.registered_chatcommands = {}
 core.chatcommands = core.registered_chatcommands -- BACKWARDS COMPATIBILITY
-function core.register_chatcommand(cmd, def)
-       def = def or {}
-       def.params = def.params or ""
-       def.description = def.description or ""
-       def.privs = def.privs or {}
-       def.mod_origin = core.get_current_modname() or "??"
-       core.registered_chatcommands[cmd] = def
-end
-
-function core.unregister_chatcommand(name)
-       if core.registered_chatcommands[name] then
-               core.registered_chatcommands[name] = nil
-       else
-               core.log("warning", "Not unregistering chatcommand " ..name..
-                       " because it doesn't exist.")
-       end
-end
 
-function core.override_chatcommand(name, redefinition)
-       local chatcommand = core.registered_chatcommands[name]
-       assert(chatcommand, "Attempt to override non-existent chatcommand "..name)
-       for k, v in pairs(redefinition) do
-               rawset(chatcommand, k, v)
+core.register_on_chat_message(function(name, message)
+       if message:sub(1,1) ~= "/" then
+               return
        end
-       core.registered_chatcommands[name] = chatcommand
-end
 
-core.register_on_chat_message(function(name, message)
        local cmd, param = string.match(message, "^/([^ ]+) *(.*)")
-       if not param then
-               param = ""
+       if not cmd then
+               core.chat_send_player(name, "-!- Empty command")
+               return true
        end
+
+       param = param or ""
+
        local cmd_def = core.registered_chatcommands[cmd]
        if not cmd_def then
-               return false
+               core.chat_send_player(name, "-!- Invalid command: " .. cmd)
+               return true
        end
        local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs)
        if has_privs then
@@ -57,7 +39,7 @@ core.register_on_chat_message(function(name, message)
        return true  -- Handled chat message
 end)
 
-if core.setting_getbool("profiler.load") then
+if core.settings:get_bool("profiler.load") then
        -- Run after register_chatcommand and its register_on_chat_message
        -- Before any chattcommands that should be profiled
        profiler.init_chatcommand()
@@ -100,7 +82,7 @@ core.register_chatcommand("me", {
 core.register_chatcommand("admin", {
        description = "Show the name of the server owner",
        func = function(name)
-               local admin = minetest.setting_get("name")
+               local admin = minetest.settings:get("name")
                if admin then
                        return true, "The administrator of this server is "..admin.."."
                else
@@ -109,63 +91,8 @@ core.register_chatcommand("admin", {
        end,
 })
 
-core.register_chatcommand("help", {
-       privs = {},
-       params = "[all/privs/<cmd>]",
-       description = "Get help for commands or list privileges",
-       func = function(name, param)
-               local function format_help_line(cmd, def)
-                       local msg = core.colorize("#00ffff", "/"..cmd)
-                       if def.params and def.params ~= "" then
-                               msg = msg .. " " .. def.params
-                       end
-                       if def.description and def.description ~= "" then
-                               msg = msg .. ": " .. def.description
-                       end
-                       return msg
-               end
-               if param == "" then
-                       local msg = ""
-                       local cmds = {}
-                       for cmd, def in pairs(core.registered_chatcommands) do
-                               if core.check_player_privs(name, def.privs) then
-                                       cmds[#cmds + 1] = cmd
-                               end
-                       end
-                       table.sort(cmds)
-                       return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"
-                                       .. "Use '/help <cmd>' to get more information,"
-                                       .. " or '/help all' to list everything."
-               elseif param == "all" then
-                       local cmds = {}
-                       for cmd, def in pairs(core.registered_chatcommands) do
-                               if core.check_player_privs(name, def.privs) then
-                                       cmds[#cmds + 1] = format_help_line(cmd, def)
-                               end
-                       end
-                       table.sort(cmds)
-                       return true, "Available commands:\n"..table.concat(cmds, "\n")
-               elseif param == "privs" then
-                       local privs = {}
-                       for priv, def in pairs(core.registered_privileges) do
-                               privs[#privs + 1] = priv .. ": " .. def.description
-                       end
-                       table.sort(privs)
-                       return true, "Available privileges:\n"..table.concat(privs, "\n")
-               else
-                       local cmd = param
-                       local def = core.registered_chatcommands[cmd]
-                       if not def then
-                               return false, "Command not available: "..cmd
-                       else
-                               return true, format_help_line(cmd, def)
-                       end
-               end
-       end,
-})
-
 core.register_chatcommand("privs", {
-       params = "<name>",
+       params = "[<name>]",
        description = "Print privileges of player",
        func = function(caller, param)
                param = param:trim()
@@ -192,7 +119,7 @@ local function handle_grant_command(caller, grantname, grantprivstr)
        local privs = core.get_player_privs(grantname)
        local privs_unknown = ""
        local basic_privs =
-               core.string_to_privs(core.setting_get("basic_privs") or "interact,shout")
+               core.string_to_privs(core.settings:get("basic_privs") or "interact,shout")
        for priv, _ in pairs(grantprivs) do
                if not basic_privs[priv] and not caller_privs.privs then
                        return false, "Your privileges are insufficient."
@@ -205,6 +132,9 @@ local function handle_grant_command(caller, grantname, grantprivstr)
        if privs_unknown ~= "" then
                return false, privs_unknown
        end
+       for priv, _ in pairs(grantprivs) do
+               core.run_priv_callbacks(grantname, priv, caller, "grant")
+       end
        core.set_player_privs(grantname, privs)
        core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
        if grantname ~= caller then
@@ -218,7 +148,7 @@ local function handle_grant_command(caller, grantname, grantprivstr)
 end
 
 core.register_chatcommand("grant", {
-       params = "<name> <privilege>|all",
+       params = "<name> (<privilege> | all)",
        description = "Give privilege to player",
        func = function(name, param)
                local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
@@ -230,7 +160,7 @@ core.register_chatcommand("grant", {
 })
 
 core.register_chatcommand("grantme", {
-       params = "<privilege>|all",
+       params = "<privilege> | all",
        description = "Grant privileges to yourself",
        func = function(name, param)
                if param == "" then
@@ -241,7 +171,7 @@ core.register_chatcommand("grantme", {
 })
 
 core.register_chatcommand("revoke", {
-       params = "<name> <privilege>|all",
+       params = "<name> (<privilege> | all)",
        description = "Remove privilege from player",
        privs = {},
        func = function(name, param)
@@ -258,7 +188,7 @@ core.register_chatcommand("revoke", {
                local revoke_privs = core.string_to_privs(revoke_priv_str)
                local privs = core.get_player_privs(revoke_name)
                local basic_privs =
-                       core.string_to_privs(core.setting_get("basic_privs") or "interact,shout")
+                       core.string_to_privs(core.settings:get("basic_privs") or "interact,shout")
                for priv, _ in pairs(revoke_privs) do
                        if not basic_privs[priv] and
                                        not core.check_player_privs(name, {privs=true}) then
@@ -266,12 +196,18 @@ core.register_chatcommand("revoke", {
                        end
                end
                if revoke_priv_str == "all" then
+                       revoke_privs = privs
                        privs = {}
                else
                        for priv, _ in pairs(revoke_privs) do
                                privs[priv] = nil
                        end
                end
+
+               for priv, _ in pairs(revoke_privs) do
+                       core.run_priv_callbacks(revoke_name, priv, name, "revoke")
+               end
+
                core.set_player_privs(revoke_name, privs)
                core.log("action", name..' revoked ('
                                ..core.privs_to_string(revoke_privs, ', ')
@@ -352,8 +288,33 @@ core.register_chatcommand("auth_reload", {
        end,
 })
 
+core.register_chatcommand("remove_player", {
+       params = "<name>",
+       description = "Remove player data",
+       privs = {server=true},
+       func = function(name, param)
+               local toname = param
+               if toname == "" then
+                       return false, "Name field required"
+               end
+
+               local rc = core.remove_player(toname)
+
+               if rc == 0 then
+                       core.log("action", name .. " removed player data of " .. toname .. ".")
+                       return true, "Player \"" .. toname .. "\" removed."
+               elseif rc == 1 then
+                       return true, "No such player \"" .. toname .. "\" to remove."
+               elseif rc == 2 then
+                       return true, "Player \"" .. toname .. "\" is connected, cannot remove."
+               end
+
+               return false, "Unhandled remove_player return code " .. rc .. ""
+       end,
+})
+
 core.register_chatcommand("teleport", {
-       params = "<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>",
+       params = "<X>,<Y>,<Z> | <to_name> | (<name> <X>,<Y>,<Z>) | (<name> <to_name>)",
        description = "Teleport to player or position",
        privs = {teleport=true},
        func = function(name, param)
@@ -385,7 +346,7 @@ core.register_chatcommand("teleport", {
                p.y = tonumber(p.y)
                p.z = tonumber(p.z)
                if p.x and p.y and p.z then
-                       local lm = tonumber(minetest.setting_get("map_generation_limit") or 31000)
+                       local lm = 31000
                        if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm or p.z < -lm or p.z > lm then
                                return false, "Cannot teleport out of map bounds!"
                        end
@@ -461,26 +422,26 @@ core.register_chatcommand("teleport", {
 })
 
 core.register_chatcommand("set", {
-       params = "[-n] <name> <value> | <name>",
+       params = "([-n] <name> <value>) | <name>",
        description = "Set or read server configuration setting",
        privs = {server=true},
        func = function(name, param)
                local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)")
                if arg and arg == "-n" and setname and setvalue then
-                       core.setting_set(setname, setvalue)
+                       core.settings:set(setname, setvalue)
                        return true, setname .. " = " .. setvalue
                end
                local setname, setvalue = string.match(param, "([^ ]+) (.+)")
                if setname and setvalue then
-                       if not core.setting_get(setname) then
+                       if not core.settings:get(setname) then
                                return false, "Failed. Use '/set -n <name> <value>' to create a new setting."
                        end
-                       core.setting_set(setname, setvalue)
+                       core.settings:set(setname, setvalue)
                        return true, setname .. " = " .. setvalue
                end
                local setname = string.match(param, "([^ ]+)")
                if setname then
-                       local setvalue = core.setting_get(setname)
+                       local setvalue = core.settings:get(setname)
                        if not setvalue then
                                setvalue = "<not set>"
                        end
@@ -516,9 +477,9 @@ local function emergeblocks_progress_update(ctx)
 end
 
 core.register_chatcommand("emergeblocks", {
-       params = "(here [radius]) | (<pos1> <pos2>)",
+       params = "(here [<radius>]) | (<pos1> <pos2>)",
        description = "Load (or, if nonexistent, generate) map blocks "
-               .. "contained in area pos1 to pos2",
+               .. "contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)",
        privs = {server=true},
        func = function(name, param)
                local p1, p2 = parse_range_str(name, param)
@@ -542,8 +503,9 @@ core.register_chatcommand("emergeblocks", {
 })
 
 core.register_chatcommand("deleteblocks", {
-       params = "(here [radius]) | (<pos1> <pos2>)",
-       description = "Delete map blocks contained in area pos1 to pos2",
+       params = "(here [<radius>]) | (<pos1> <pos2>)",
+       description = "Delete map blocks contained in area pos1 to pos2 "
+               .. "(<pos1> and <pos2> must be in parentheses)",
        privs = {server=true},
        func = function(name, param)
                local p1, p2 = parse_range_str(name, param)
@@ -560,6 +522,26 @@ core.register_chatcommand("deleteblocks", {
        end,
 })
 
+core.register_chatcommand("fixlight", {
+       params = "(here [<radius>]) | (<pos1> <pos2>)",
+       description = "Resets lighting in the area between pos1 and pos2 "
+               .. "(<pos1> and <pos2> must be in parentheses)",
+       privs = {server = true},
+       func = function(name, param)
+               local p1, p2 = parse_range_str(name, param)
+               if p1 == false then
+                       return false, p2
+               end
+
+               if core.fix_light(p1, p2) then
+                       return true, "Successfully reset light in the area ranging from " ..
+                               core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1)
+               else
+                       return false, "Failed to load one or more blocks in area"
+               end
+       end,
+})
+
 core.register_chatcommand("mods", {
        params = "",
        description = "List mods installed on the server",
@@ -647,6 +629,9 @@ core.register_chatcommand("spawnentity", {
                        core.log("error", "Unable to spawn entity, player is nil")
                        return false, "Unable to spawn entity, player is nil"
                end
+               if not minetest.registered_entities[entityname] then
+                       return false, "Cannot spawn an unknown entity"
+               end
                if p == "" then
                        p = player:getpos()
                else
@@ -682,21 +667,21 @@ core.register_chatcommand("pulverize", {
 core.rollback_punch_callbacks = {}
 
 core.register_on_punchnode(function(pos, node, puncher)
-       local name = puncher:get_player_name()
-       if core.rollback_punch_callbacks[name] then
+       local name = puncher and puncher:get_player_name()
+       if name and core.rollback_punch_callbacks[name] then
                core.rollback_punch_callbacks[name](pos, node, puncher)
                core.rollback_punch_callbacks[name] = nil
        end
 end)
 
 core.register_chatcommand("rollback_check", {
-       params = "[<range>] [<seconds>] [limit]",
+       params = "[<range>] [<seconds>] [<limit>]",
        description = "Check who last touched a node or a node near it"
                        .. " within the time specified by <seconds>. Default: range = 0,"
                        .. " seconds = 86400 = 24h, limit = 5",
        privs = {rollback=true},
        func = function(name, param)
-               if not core.setting_getbool("enable_rollback_recording") then
+               if not core.settings:get_bool("enable_rollback_recording") then
                        return false, "Rollback functions are disabled."
                end
                local range, seconds, limit =
@@ -743,11 +728,11 @@ core.register_chatcommand("rollback_check", {
 })
 
 core.register_chatcommand("rollback", {
-       params = "<player name> [<seconds>] | :<actor> [<seconds>]",
+       params = "(<name> [<seconds>]) | (:<actor> [<seconds>])",
        description = "Revert actions of a player. Default for <seconds> is 60",
        privs = {rollback=true},
        func = function(name, param)
-               if not core.setting_getbool("enable_rollback_recording") then
+               if not core.settings:get_bool("enable_rollback_recording") then
                        return false, "Rollback functions are disabled."
                end
                local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
@@ -835,15 +820,21 @@ core.register_chatcommand("days", {
 })
 
 core.register_chatcommand("shutdown", {
-       description = "Shutdown server",
-       params = "[reconnect] [message]",
+       params = "[<delay_in_seconds> | -1] [reconnect] [<message>]",
+       description = "Shutdown server (-1 cancels a delayed shutdown)",
        privs = {server=true},
        func = function(name, param)
-               core.log("action", name .. " shuts down server")
-               core.chat_send_all("*** Server shutting down (operator request).")
-               local reconnect, message = param:match("([^ ]+)(.*)")
+               local delay, reconnect, message = param:match("([^ ][-]?[0-9]+)([^ ]+)(.*)")
                message = message or ""
-               core.request_shutdown(message:trim(), core.is_yes(reconnect))
+
+               if delay ~= "" then
+                       delay = tonumber(param) or 0
+               else
+                       delay = 0
+                       core.log("action", name .. " shuts down server")
+                       core.chat_send_all("*** Server shutting down (operator request).")
+               end
+               core.request_shutdown(message:trim(), core.is_yes(reconnect), delay)
        end,
 })
 
@@ -868,7 +859,7 @@ core.register_chatcommand("ban", {
 })
 
 core.register_chatcommand("unban", {
-       params = "<name/ip>",
+       params = "<name> | <IP_address>",
        description = "Remove IP ban",
        privs = {ban=true},
        func = function(name, param)
@@ -881,7 +872,7 @@ core.register_chatcommand("unban", {
 })
 
 core.register_chatcommand("kick", {
-       params = "<name> [reason]",
+       params = "<name> [<reason>]",
        description = "Kick a player",
        privs = {kick=true},
        func = function(name, param)
@@ -900,7 +891,7 @@ core.register_chatcommand("kick", {
 })
 
 core.register_chatcommand("clearobjects", {
-       params = "[full|quick]",
+       params = "[full | quick]",
        description = "Clear all objects in world",
        privs = {server=true},
        func = function(name, param)
@@ -946,7 +937,7 @@ core.register_chatcommand("msg", {
 })
 
 core.register_chatcommand("last-login", {
-       params = "[name]",
+       params = "[<name>]",
        description = "Get the last login time of a player",
        func = function(name, param)
                if param == "" then
@@ -961,3 +952,31 @@ core.register_chatcommand("last-login", {
                return false, "Last login time is unknown"
        end,
 })
+
+core.register_chatcommand("clearinv", {
+       params = "[<name>]",
+       description = "Clear the inventory of yourself or another player",
+       func = function(name, param)
+               local player
+               if param and param ~= "" and param ~= name then
+                       if not core.check_player_privs(name, {server=true}) then
+                               return false, "You don't have permission"
+                                               .. " to run this command (missing privilege: server)"
+                       end
+                       player = core.get_player_by_name(param)
+                       core.chat_send_player(param, name.." cleared your inventory.")
+               else
+                       player = core.get_player_by_name(name)
+               end
+
+               if player then
+                       player:get_inventory():set_list("main", {})
+                       player:get_inventory():set_list("craft", {})
+                       player:get_inventory():set_list("craftpreview", {})
+                       core.log("action", name.." clears "..player:get_player_name().."'s inventory")
+                       return true, "Cleared "..player:get_player_name().."'s inventory."
+               else
+                       return false, "Player must be online to clear inventory!"
+               end
+       end,
+})