]> git.lizzy.rs Git - minetest.git/blobdiff - builtin/game/chatcommands.lua
Sounds: Add falling node sounds
[minetest.git] / builtin / game / chatcommands.lua
index 3350140eea918d608d5cccef7f997ba23b6e8c58..cbfe2fb257bb82412a26fa0228f1116f71e0d352 100644 (file)
@@ -1,40 +1,28 @@
--- Minetest: builtin/chatcommands.lua
+-- Minetest: builtin/game/chatcommands.lua
 
 --
 -- Chat command handler
 --
 
 
 --
 -- Chat command handler
 --
 
-core.chatcommands = {}
-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.chatcommands[cmd] = def
-end
-
-if core.setting_getbool("mod_profiling") then
-       local tracefct = profiling_print_log
-       profiling_print_log = nil
-       core.register_chatcommand("save_mod_profile",
-                       {
-                               params      = "",
-                               description = "save mod profiling data to logfile " ..
-                                               "(depends on default loglevel)",
-                               func        = tracefct,
-                               privs       = { server=true }
-                       })
-end
+core.chatcommands = core.registered_chatcommands -- BACKWARDS COMPATIBILITY
 
 core.register_on_chat_message(function(name, message)
 
 core.register_on_chat_message(function(name, message)
+       if message:sub(1,1) ~= "/" then
+               return
+       end
+
        local cmd, param = string.match(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
        end
-       local cmd_def = core.chatcommands[cmd]
+
+       param = param or ""
+
+       local cmd_def = core.registered_chatcommands[cmd]
        if not cmd_def then
        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
        end
        local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs)
        if has_privs then
@@ -51,6 +39,12 @@ core.register_on_chat_message(function(name, message)
        return true  -- Handled chat message
 end)
 
        return true  -- Handled chat message
 end)
 
+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()
+end
+
 -- Parses a "range" string in the format of "here (number)" or
 -- "(x1, y1, z1) (x2, y2, z2)", returning two position vectors
 local function parse_range_str(player_name, str)
 -- Parses a "range" string in the format of "here (number)" or
 -- "(x1, y1, z1) (x2, y2, z2)", returning two position vectors
 local function parse_range_str(player_name, str)
@@ -77,7 +71,8 @@ end
 --
 core.register_chatcommand("me", {
        params = "<action>",
 --
 core.register_chatcommand("me", {
        params = "<action>",
-       description = "chat action (eg. /me orders a pizza)",
+       description = "Display chat action (e.g., '/me orders a pizza' displays"
+                       .. " '<player name> orders a pizza')",
        privs = {shout=true},
        func = function(name, param)
                core.chat_send_all("* " .. name .. " " .. param)
        privs = {shout=true},
        func = function(name, param)
                core.chat_send_all("* " .. name .. " " .. param)
@@ -87,7 +82,7 @@ core.register_chatcommand("me", {
 core.register_chatcommand("admin", {
        description = "Show the name of the server owner",
        func = function(name)
 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
                if admin then
                        return true, "The administrator of this server is "..admin.."."
                else
@@ -96,120 +91,87 @@ core.register_chatcommand("admin", {
        end,
 })
 
        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 = "/"..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.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.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.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", {
 core.register_chatcommand("privs", {
-       params = "<name>",
-       description = "print out privileges of player",
-       func = function(name, param)
-               param = (param ~= "" and param or name)
-               return true, "Privileges of " .. param .. ": "
+       params = "[<name>]",
+       description = "Print privileges of player",
+       func = function(caller, param)
+               param = param:trim()
+               local name = (param ~= "" and param or caller)
+               return true, "Privileges of " .. name .. ": "
                        .. core.privs_to_string(
                        .. core.privs_to_string(
-                               core.get_player_privs(param), ' ')
+                               core.get_player_privs(name), ' ')
        end,
 })
        end,
 })
+
+local function handle_grant_command(caller, grantname, grantprivstr)
+       local caller_privs = minetest.get_player_privs(caller)
+       if not (caller_privs.privs or caller_privs.basic_privs) then
+               return false, "Your privileges are insufficient."
+       end
+
+       if not core.get_auth_handler().get_auth(grantname) then
+               return false, "Player " .. grantname .. " does not exist."
+       end
+       local grantprivs = core.string_to_privs(grantprivstr)
+       if grantprivstr == "all" then
+               grantprivs = core.registered_privileges
+       end
+       local privs = core.get_player_privs(grantname)
+       local privs_unknown = ""
+       local basic_privs =
+               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."
+               end
+               if not core.registered_privileges[priv] then
+                       privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n"
+               end
+               privs[priv] = true
+       end
+       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
+               core.chat_send_player(grantname, caller
+                               .. " granted you privileges: "
+                               .. core.privs_to_string(grantprivs, ' '))
+       end
+       return true, "Privileges of " .. grantname .. ": "
+               .. core.privs_to_string(
+                       core.get_player_privs(grantname), ' ')
+end
+
 core.register_chatcommand("grant", {
 core.register_chatcommand("grant", {
-       params = "<name> <privilege>|all",
+       params = "<name> (<privilege> | all)",
        description = "Give privilege to player",
        func = function(name, param)
        description = "Give privilege to player",
        func = function(name, param)
-               if not core.check_player_privs(name, {privs=true}) and
-                               not core.check_player_privs(name, {basic_privs=true}) then
-                       return false, "Your privileges are insufficient."
-               end
                local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
                if not grantname or not grantprivstr then
                        return false, "Invalid parameters (see /help grant)"
                local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
                if not grantname or not grantprivstr then
                        return false, "Invalid parameters (see /help grant)"
-               elseif not core.auth_table[grantname] then
-                       return false, "Player " .. grantname .. " does not exist."
-               end
-               local grantprivs = core.string_to_privs(grantprivstr)
-               if grantprivstr == "all" then
-                       grantprivs = core.registered_privileges
-               end
-               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")
-               for priv, _ in pairs(grantprivs) do
-                       if not basic_privs[priv] and
-                                       not core.check_player_privs(name, {privs=true}) then
-                               return false, "Your privileges are insufficient."
-                       end
-                       if not core.registered_privileges[priv] then
-                               privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n"
-                       end
-                       privs[priv] = true
-               end
-               if privs_unknown ~= "" then
-                       return false, privs_unknown
                end
                end
-               core.set_player_privs(grantname, privs)
-               core.log("action", name..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
-               if grantname ~= name then
-                       core.chat_send_player(grantname, name
-                                       .. " granted you privileges: "
-                                       .. core.privs_to_string(grantprivs, ' '))
+               return handle_grant_command(name, grantname, grantprivstr)
+       end,
+})
+
+core.register_chatcommand("grantme", {
+       params = "<privilege> | all",
+       description = "Grant privileges to yourself",
+       func = function(name, param)
+               if param == "" then
+                       return false, "Invalid parameters (see /help grantme)"
                end
                end
-               return true, "Privileges of " .. grantname .. ": "
-                       .. core.privs_to_string(
-                               core.get_player_privs(grantname), ' ')
+               return handle_grant_command(name, name, param)
        end,
 })
        end,
 })
+
 core.register_chatcommand("revoke", {
 core.register_chatcommand("revoke", {
-       params = "<name> <privilege>|all",
+       params = "<name> (<privilege> | all)",
        description = "Remove privilege from player",
        privs = {},
        func = function(name, param)
        description = "Remove privilege from player",
        privs = {},
        func = function(name, param)
@@ -220,13 +182,13 @@ core.register_chatcommand("revoke", {
                local revoke_name, revoke_priv_str = string.match(param, "([^ ]+) (.+)")
                if not revoke_name or not revoke_priv_str then
                        return false, "Invalid parameters (see /help revoke)"
                local revoke_name, revoke_priv_str = string.match(param, "([^ ]+) (.+)")
                if not revoke_name or not revoke_priv_str then
                        return false, "Invalid parameters (see /help revoke)"
-               elseif not core.auth_table[revoke_name] then
+               elseif not core.get_auth_handler().get_auth(revoke_name) then
                        return false, "Player " .. revoke_name .. " does not exist."
                end
                local revoke_privs = core.string_to_privs(revoke_priv_str)
                local privs = core.get_player_privs(revoke_name)
                local basic_privs =
                        return false, "Player " .. revoke_name .. " does not exist."
                end
                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
                for priv, _ in pairs(revoke_privs) do
                        if not basic_privs[priv] and
                                        not core.check_player_privs(name, {privs=true}) then
@@ -234,12 +196,18 @@ core.register_chatcommand("revoke", {
                        end
                end
                if revoke_priv_str == "all" then
                        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
                        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, ', ')
                core.set_player_privs(revoke_name, privs)
                core.log("action", name..' revoked ('
                                ..core.privs_to_string(revoke_privs, ', ')
@@ -257,7 +225,7 @@ core.register_chatcommand("revoke", {
 
 core.register_chatcommand("setpassword", {
        params = "<name> <password>",
 
 core.register_chatcommand("setpassword", {
        params = "<name> <password>",
-       description = "set given password",
+       description = "Set player's password",
        privs = {password=true},
        func = function(name, param)
                local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$")
        privs = {password=true},
        func = function(name, param)
                local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$")
@@ -295,7 +263,7 @@ core.register_chatcommand("setpassword", {
 
 core.register_chatcommand("clearpassword", {
        params = "<name>",
 
 core.register_chatcommand("clearpassword", {
        params = "<name>",
-       description = "set empty password",
+       description = "Set empty password",
        privs = {password=true},
        func = function(name, param)
                local toname = param
        privs = {password=true},
        func = function(name, param)
                local toname = param
@@ -312,7 +280,7 @@ core.register_chatcommand("clearpassword", {
 
 core.register_chatcommand("auth_reload", {
        params = "",
 
 core.register_chatcommand("auth_reload", {
        params = "",
-       description = "reload authentication data",
+       description = "Reload authentication data",
        privs = {server=true},
        func = function(name, param)
                local done = core.auth_reload()
        privs = {server=true},
        func = function(name, param)
                local done = core.auth_reload()
@@ -320,9 +288,34 @@ core.register_chatcommand("auth_reload", {
        end,
 })
 
        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", {
 core.register_chatcommand("teleport", {
-       params = "<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>",
-       description = "teleport to given position",
+       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)
                -- Returns (pos, true) if found, otherwise (pos, false)
        privs = {teleport=true},
        func = function(name, param)
                -- Returns (pos, true) if found, otherwise (pos, false)
@@ -353,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
                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
                        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
@@ -429,26 +422,26 @@ core.register_chatcommand("teleport", {
 })
 
 core.register_chatcommand("set", {
 })
 
 core.register_chatcommand("set", {
-       params = "[-n] <name> <value> | <name>",
-       description = "set or read server configuration setting",
+       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
        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
                        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
                                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
                        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
                        if not setvalue then
                                setvalue = "<not set>"
                        end
@@ -484,9 +477,9 @@ local function emergeblocks_progress_update(ctx)
 end
 
 core.register_chatcommand("emergeblocks", {
 end
 
 core.register_chatcommand("emergeblocks", {
-       params = "(here [radius]) | (<pos1> <pos2>)",
-       description = "starts loading (or generating, if inexistent) map blocks "
-               .. "contained in area pos1 to pos2",
+       params = "(here [<radius>]) | (<pos1> <pos2>)",
+       description = "Load (or, if nonexistent, generate) 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)
        privs = {server=true},
        func = function(name, param)
                local p1, p2 = parse_range_str(name, param)
@@ -510,8 +503,9 @@ core.register_chatcommand("emergeblocks", {
 })
 
 core.register_chatcommand("deleteblocks", {
 })
 
 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)
        privs = {server=true},
        func = function(name, param)
                local p1, p2 = parse_range_str(name, param)
@@ -528,6 +522,26 @@ core.register_chatcommand("deleteblocks", {
        end,
 })
 
        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",
 core.register_chatcommand("mods", {
        params = "",
        description = "List mods installed on the server",
@@ -575,7 +589,7 @@ end
 
 core.register_chatcommand("give", {
        params = "<name> <ItemString>",
 
 core.register_chatcommand("give", {
        params = "<name> <ItemString>",
-       description = "give item to player",
+       description = "Give item to player",
        privs = {give=true},
        func = function(name, param)
                local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$")
        privs = {give=true},
        func = function(name, param)
                local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$")
@@ -588,7 +602,7 @@ core.register_chatcommand("give", {
 
 core.register_chatcommand("giveme", {
        params = "<ItemString>",
 
 core.register_chatcommand("giveme", {
        params = "<ItemString>",
-       description = "give item to yourself",
+       description = "Give item to yourself",
        privs = {give=true},
        func = function(name, param)
                local itemstring = string.match(param, "(.+)$")
        privs = {give=true},
        func = function(name, param)
                local itemstring = string.match(param, "(.+)$")
@@ -615,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
                        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
                if p == "" then
                        p = player:getpos()
                else
@@ -650,21 +667,21 @@ core.register_chatcommand("pulverize", {
 core.rollback_punch_callbacks = {}
 
 core.register_on_punchnode(function(pos, node, puncher)
 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", {
                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]",
-       description = "Check who has last touched a node or near it,"
-                       .. " max. <seconds> ago (default range=0,"
-                       .. " seconds=86400=24h, limit=5)",
+       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)
        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 =
                        return false, "Rollback functions are disabled."
                end
                local range, seconds, limit =
@@ -711,11 +728,11 @@ core.register_chatcommand("rollback_check", {
 })
 
 core.register_chatcommand("rollback", {
 })
 
 core.register_chatcommand("rollback", {
-       params = "<player name> [<seconds>] | :<actor> [<seconds>]",
-       description = "revert actions of a player; default for <seconds> is 60",
+       params = "(<name> [<seconds>]) | (:<actor> [<seconds>])",
+       description = "Revert actions of a player. Default for <seconds> is 60",
        privs = {rollback=true},
        func = function(name, param)
        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*)")
                        return false, "Rollback functions are disabled."
                end
                local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
@@ -757,7 +774,7 @@ core.register_chatcommand("status", {
 
 core.register_chatcommand("time", {
        params = "<0..23>:<0..59> | <0..24000>",
 
 core.register_chatcommand("time", {
        params = "<0..23>:<0..59> | <0..24000>",
-       description = "set time of day",
+       description = "Set time of day",
        privs = {},
        func = function(name, param)
                if param == "" then
        privs = {},
        func = function(name, param)
                if param == "" then
@@ -803,12 +820,21 @@ core.register_chatcommand("days", {
 })
 
 core.register_chatcommand("shutdown", {
 })
 
 core.register_chatcommand("shutdown", {
-       description = "shutdown server",
+       params = "[<delay_in_seconds> | -1] [reconnect] [<message>]",
+       description = "Shutdown server (-1 cancels a delayed shutdown)",
        privs = {server=true},
        func = function(name, param)
        privs = {server=true},
        func = function(name, param)
-               core.log("action", name .. " shuts down server")
-               core.request_shutdown()
-               core.chat_send_all("*** Server shutting down (operator request).")
+               local delay, reconnect, message = param:match("([^ ][-]?[0-9]+)([^ ]+)(.*)")
+               message = message or ""
+
+               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,
 })
 
        end,
 })
 
@@ -833,8 +859,8 @@ core.register_chatcommand("ban", {
 })
 
 core.register_chatcommand("unban", {
 })
 
 core.register_chatcommand("unban", {
-       params = "<name/ip>",
-       description = "remove IP ban",
+       params = "<name> | <IP_address>",
+       description = "Remove IP ban",
        privs = {ban=true},
        func = function(name, param)
                if not core.unban_player_or_ip(param) then
        privs = {ban=true},
        func = function(name, param)
                if not core.unban_player_or_ip(param) then
@@ -846,8 +872,8 @@ core.register_chatcommand("unban", {
 })
 
 core.register_chatcommand("kick", {
 })
 
 core.register_chatcommand("kick", {
-       params = "<name> [reason]",
-       description = "kick a player",
+       params = "<name> [<reason>]",
+       description = "Kick a player",
        privs = {kick=true},
        func = function(name, param)
                local tokick, reason = param:match("([^ ]+) (.+)")
        privs = {kick=true},
        func = function(name, param)
                local tokick, reason = param:match("([^ ]+) (.+)")
@@ -865,8 +891,8 @@ core.register_chatcommand("kick", {
 })
 
 core.register_chatcommand("clearobjects", {
 })
 
 core.register_chatcommand("clearobjects", {
-       params = "[full|quick]",
-       description = "clear all objects in world",
+       params = "[full | quick]",
+       description = "Clear all objects in world",
        privs = {server=true},
        func = function(name, param)
                local options = {}
        privs = {server=true},
        func = function(name, param)
                local options = {}
@@ -911,7 +937,7 @@ core.register_chatcommand("msg", {
 })
 
 core.register_chatcommand("last-login", {
 })
 
 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
        description = "Get the last login time of a player",
        func = function(name, param)
                if param == "" then
@@ -926,3 +952,31 @@ core.register_chatcommand("last-login", {
                return false, "Last login time is unknown"
        end,
 })
                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,
+})