]> git.lizzy.rs Git - minetest.git/blobdiff - builtin/game/chatcommands.lua
Builtin: Forward old return values
[minetest.git] / builtin / game / chatcommands.lua
index 745b012e6f03be15bcd7af59bef960c3387afcf5..60d5d4788bf19497752a5ee6aeab04a2fe260e82 100644 (file)
@@ -7,13 +7,22 @@
 core.chatcommands = core.registered_chatcommands -- BACKWARDS COMPATIBILITY
 
 core.register_on_chat_message(function(name, message)
 core.chatcommands = core.registered_chatcommands -- BACKWARDS COMPATIBILITY
 
 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
+
+       param = param or ""
+
        local cmd_def = core.registered_chatcommands[cmd]
        if not cmd_def then
        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
        end
        local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs)
        if has_privs then
@@ -30,9 +39,9 @@ core.register_on_chat_message(function(name, message)
        return true  -- Handled chat message
 end)
 
        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
        -- Run after register_chatcommand and its register_on_chat_message
-       -- Before any chattcommands that should be profiled
+       -- Before any chatcommands that should be profiled
        profiler.init_chatcommand()
 end
 
        profiler.init_chatcommand()
 end
 
@@ -62,7 +71,7 @@ end
 --
 core.register_chatcommand("me", {
        params = "<action>",
 --
 core.register_chatcommand("me", {
        params = "<action>",
-       description = "Display chat action (e.g., '/me orders a pizza' displays"
+       description = "Show chat action (e.g., '/me orders a pizza' displays"
                        .. " '<player name> orders a pizza')",
        privs = {shout=true},
        func = function(name, param)
                        .. " '<player name> orders a pizza')",
        privs = {shout=true},
        func = function(name, param)
@@ -73,84 +82,57 @@ 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 = core.settings:get("name")
                if admin then
                if admin then
-                       return true, "The administrator of this server is "..admin.."."
+                       return true, "The administrator of this server is " .. admin .. "."
                else
                        return false, "There's no administrator named in the config file."
                end
        end,
 })
 
                else
                        return false, "There's no administrator named in the config file."
                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 = 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", {
 core.register_chatcommand("privs", {
-       params = "<name>",
-       description = "Print privileges of player",
+       params = "[<name>]",
+       description = "Show privileges of yourself or another player",
        func = function(caller, param)
                param = param:trim()
                local name = (param ~= "" and param or caller)
        func = function(caller, param)
                param = param:trim()
                local name = (param ~= "" and param or caller)
+               if not core.player_exists(name) then
+                       return false, "Player " .. name .. " does not exist."
+               end
                return true, "Privileges of " .. name .. ": "
                        .. core.privs_to_string(
                                core.get_player_privs(name), ' ')
        end,
 })
 
                return true, "Privileges of " .. name .. ": "
                        .. core.privs_to_string(
                                core.get_player_privs(name), ' ')
        end,
 })
 
+core.register_chatcommand("haspriv", {
+       params = "<privilege>",
+       description = "Return list of all online players with privilege.",
+       privs = {basic_privs = true},
+       func = function(caller, param)
+               param = param:trim()
+               if param == "" then
+                       return false, "Invalid parameters (see /help haspriv)"
+               end
+               if not core.registered_privileges[param] then
+                       return false, "Unknown privilege!"
+               end
+               local privs = core.string_to_privs(param)
+               local players_with_priv = {}
+               for _, player in pairs(core.get_connected_players()) do
+                       local player_name = player:get_player_name()
+                       if core.check_player_privs(player_name, privs) then
+                               table.insert(players_with_priv, player_name)
+                       end
+               end     
+               return true, "Players online with the \"" .. param .. "\" privilege: " ..
+                       table.concat(players_with_priv, ", ")
+       end     
+})
+
 local function handle_grant_command(caller, grantname, grantprivstr)
 local function handle_grant_command(caller, grantname, grantprivstr)
-       local caller_privs = minetest.get_player_privs(caller)
+       local caller_privs = core.get_player_privs(caller)
        if not (caller_privs.privs or caller_privs.basic_privs) then
                return false, "Your privileges are insufficient."
        end
        if not (caller_privs.privs or caller_privs.basic_privs) then
                return false, "Your privileges are insufficient."
        end
@@ -165,7 +147,7 @@ local function handle_grant_command(caller, grantname, grantprivstr)
        local privs = core.get_player_privs(grantname)
        local privs_unknown = ""
        local basic_privs =
        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."
        for priv, _ in pairs(grantprivs) do
                if not basic_privs[priv] and not caller_privs.privs then
                        return false, "Your privileges are insufficient."
@@ -178,6 +160,9 @@ local function handle_grant_command(caller, grantname, grantprivstr)
        if privs_unknown ~= "" then
                return false, privs_unknown
        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.set_player_privs(grantname, privs)
        core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
        if grantname ~= caller then
@@ -191,8 +176,8 @@ local function handle_grant_command(caller, grantname, grantprivstr)
 end
 
 core.register_chatcommand("grant", {
 end
 
 core.register_chatcommand("grant", {
-       params = "<name> <privilege>|all",
-       description = "Give privilege to player",
+       params = "<name> (<privilege> | all)",
+       description = "Give privileges to player",
        func = function(name, param)
                local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
                if not grantname or not grantprivstr then
        func = function(name, param)
                local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
                if not grantname or not grantprivstr then
@@ -203,7 +188,7 @@ core.register_chatcommand("grant", {
 })
 
 core.register_chatcommand("grantme", {
 })
 
 core.register_chatcommand("grantme", {
-       params = "<privilege>|all",
+       params = "<privilege> | all",
        description = "Grant privileges to yourself",
        func = function(name, param)
                if param == "" then
        description = "Grant privileges to yourself",
        func = function(name, param)
                if param == "" then
@@ -214,8 +199,8 @@ core.register_chatcommand("grantme", {
 })
 
 core.register_chatcommand("revoke", {
 })
 
 core.register_chatcommand("revoke", {
-       params = "<name> <privilege>|all",
-       description = "Remove privilege from player",
+       params = "<name> (<privilege> | all)",
+       description = "Remove privileges from player",
        privs = {},
        func = function(name, param)
                if not core.check_player_privs(name, {privs=true}) and
        privs = {},
        func = function(name, param)
                if not core.check_player_privs(name, {privs=true}) and
@@ -231,7 +216,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 =
                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
@@ -239,12 +224,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, ', ')
@@ -300,7 +291,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 for a player",
        privs = {password=true},
        func = function(name, param)
                local toname = param
        privs = {password=true},
        func = function(name, param)
                local toname = param
@@ -325,9 +316,34 @@ core.register_chatcommand("auth_reload", {
        end,
 })
 
        end,
 })
 
+core.register_chatcommand("remove_player", {
+       params = "<name>",
+       description = "Remove a player's 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 player or position",
+       params = "<X>,<Y>,<Z> | <to_name> | (<name> <X>,<Y>,<Z>) | (<name> <to_name>)",
+       description = "Teleport to position or player",
        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)
@@ -358,13 +374,13 @@ 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
                        teleportee = core.get_player_by_name(name)
                        if teleportee then
                        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
                        teleportee = core.get_player_by_name(name)
                        if teleportee then
-                               teleportee:setpos(p)
+                               teleportee:set_pos(p)
                                return true, "Teleporting to "..core.pos_to_string(p)
                        end
                end
                                return true, "Teleporting to "..core.pos_to_string(p)
                        end
                end
@@ -377,12 +393,12 @@ core.register_chatcommand("teleport", {
                if target_name then
                        local target = core.get_player_by_name(target_name)
                        if target then
                if target_name then
                        local target = core.get_player_by_name(target_name)
                        if target then
-                               p = target:getpos()
+                               p = target:get_pos()
                        end
                end
                if teleportee and p then
                        p = find_free_position_near(p)
                        end
                end
                if teleportee and p then
                        p = find_free_position_near(p)
-                       teleportee:setpos(p)
+                       teleportee:set_pos(p)
                        return true, "Teleporting to " .. target_name
                                        .. " at "..core.pos_to_string(p)
                end
                        return true, "Teleporting to " .. target_name
                                        .. " at "..core.pos_to_string(p)
                end
@@ -401,7 +417,7 @@ core.register_chatcommand("teleport", {
                        teleportee = core.get_player_by_name(teleportee_name)
                end
                if teleportee and p.x and p.y and p.z then
                        teleportee = core.get_player_by_name(teleportee_name)
                end
                if teleportee and p.x and p.y and p.z then
-                       teleportee:setpos(p)
+                       teleportee:set_pos(p)
                        return true, "Teleporting " .. teleportee_name
                                        .. " to " .. core.pos_to_string(p)
                end
                        return true, "Teleporting " .. teleportee_name
                                        .. " to " .. core.pos_to_string(p)
                end
@@ -417,12 +433,12 @@ core.register_chatcommand("teleport", {
                if target_name then
                        local target = core.get_player_by_name(target_name)
                        if target then
                if target_name then
                        local target = core.get_player_by_name(target_name)
                        if target then
-                               p = target:getpos()
+                               p = target:get_pos()
                        end
                end
                if teleportee and p then
                        p = find_free_position_near(p)
                        end
                end
                if teleportee and p then
                        p = find_free_position_near(p)
-                       teleportee:setpos(p)
+                       teleportee:set_pos(p)
                        return true, "Teleporting " .. teleportee_name
                                        .. " to " .. target_name
                                        .. " at " .. core.pos_to_string(p)
                        return true, "Teleporting " .. teleportee_name
                                        .. " to " .. target_name
                                        .. " at " .. core.pos_to_string(p)
@@ -434,26 +450,26 @@ core.register_chatcommand("teleport", {
 })
 
 core.register_chatcommand("set", {
 })
 
 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
        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
                        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
@@ -489,9 +505,9 @@ local function emergeblocks_progress_update(ctx)
 end
 
 core.register_chatcommand("emergeblocks", {
 end
 
 core.register_chatcommand("emergeblocks", {
-       params = "(here [radius]) | (<pos1> <pos2>)",
+       params = "(here [<radius>]) | (<pos1> <pos2>)",
        description = "Load (or, if nonexistent, generate) map blocks "
        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)
        privs = {server=true},
        func = function(name, param)
                local p1, p2 = parse_range_str(name, param)
@@ -515,8 +531,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)
@@ -533,6 +550,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",
@@ -548,8 +585,11 @@ local function handle_give_command(cmd, giver, receiver, stackstring)
        local itemstack = ItemStack(stackstring)
        if itemstack:is_empty() then
                return false, "Cannot give an empty item"
        local itemstack = ItemStack(stackstring)
        if itemstack:is_empty() then
                return false, "Cannot give an empty item"
-       elseif not itemstack:is_known() then
+       elseif (not itemstack:is_known()) or (itemstack:get_name() == "unknown") then
                return false, "Cannot give an unknown item"
                return false, "Cannot give an unknown item"
+       -- Forbid giving 'ignore' due to unwanted side effects
+       elseif itemstack:get_name() == "ignore" then
+               return false, "Giving 'ignore' is not allowed"
        end
        local receiverref = core.get_player_by_name(receiver)
        if receiverref == nil then
        end
        local receiverref = core.get_player_by_name(receiver)
        if receiverref == nil then
@@ -568,18 +608,18 @@ local function handle_give_command(cmd, giver, receiver, stackstring)
        -- entered (e.g. big numbers are always interpreted as 2^16-1).
        stackstring = itemstack:to_string()
        if giver == receiver then
        -- entered (e.g. big numbers are always interpreted as 2^16-1).
        stackstring = itemstack:to_string()
        if giver == receiver then
-               return true, ("%q %sadded to inventory.")
-                               :format(stackstring, partiality)
+               local msg = "%q %sadded to inventory."
+               return true, msg:format(stackstring, partiality)
        else
                core.chat_send_player(receiver, ("%q %sadded to inventory.")
                                :format(stackstring, partiality))
        else
                core.chat_send_player(receiver, ("%q %sadded to inventory.")
                                :format(stackstring, partiality))
-               return true, ("%q %sadded to %s's inventory.")
-                               :format(stackstring, partiality, receiver)
+               local msg = "%q %sadded to %s's inventory."
+               return true, msg:format(stackstring, partiality, receiver)
        end
 end
 
 core.register_chatcommand("give", {
        end
 end
 
 core.register_chatcommand("give", {
-       params = "<name> <ItemString>",
+       params = "<name> <ItemString> [<count> [<wear>]]",
        description = "Give item to player",
        privs = {give=true},
        func = function(name, param)
        description = "Give item to player",
        privs = {give=true},
        func = function(name, param)
@@ -592,7 +632,7 @@ core.register_chatcommand("give", {
 })
 
 core.register_chatcommand("giveme", {
 })
 
 core.register_chatcommand("giveme", {
-       params = "<ItemString>",
+       params = "<ItemString> [<count> [<wear>]]",
        description = "Give item to yourself",
        privs = {give=true},
        func = function(name, param)
        description = "Give item to yourself",
        privs = {give=true},
        func = function(name, param)
@@ -620,8 +660,11 @@ 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 core.registered_entities[entityname] then
+                       return false, "Cannot spawn an unknown entity"
+               end
                if p == "" then
                if p == "" then
-                       p = player:getpos()
+                       p = player:get_pos()
                else
                        p = core.string_to_pos(p)
                        if p == nil then
                else
                        p = core.string_to_pos(p)
                        if p == nil then
@@ -643,10 +686,13 @@ core.register_chatcommand("pulverize", {
                        core.log("error", "Unable to pulverize, no player.")
                        return false, "Unable to pulverize, no player."
                end
                        core.log("error", "Unable to pulverize, no player.")
                        return false, "Unable to pulverize, no player."
                end
-               if player:get_wielded_item():is_empty() then
+               local wielded_item = player:get_wielded_item()
+               if wielded_item:is_empty() then
                        return false, "Unable to pulverize, no item in hand."
                end
                        return false, "Unable to pulverize, no item in hand."
                end
-               player:set_wielded_item(nil)
+               core.log("action", name .. " pulverized \"" ..
+                       wielded_item:get_name() .. " " .. wielded_item:get_count() .. "\"")
+               player:set_wielded_item(nil)                    
                return true, "An item was pulverized."
        end,
 })
                return true, "An item was pulverized."
        end,
 })
@@ -655,21 +701,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]",
+       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)
        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 =
                        return false, "Rollback functions are disabled."
                end
                local range, seconds, limit =
@@ -716,11 +762,11 @@ core.register_chatcommand("rollback_check", {
 })
 
 core.register_chatcommand("rollback", {
 })
 
 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)
        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*)")
                        return false, "Rollback functions are disabled."
                end
                local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
@@ -754,15 +800,19 @@ core.register_chatcommand("rollback", {
 })
 
 core.register_chatcommand("status", {
 })
 
 core.register_chatcommand("status", {
-       description = "Print server status",
+       description = "Show server status",
        func = function(name, param)
        func = function(name, param)
-               return true, core.get_server_status()
+               local status = core.get_server_status(name, false)
+               if status and status ~= "" then
+                       return true, status
+               end
+               return false, "This command was disabled by a mod or game"
        end,
 })
 
 core.register_chatcommand("time", {
        end,
 })
 
 core.register_chatcommand("time", {
-       params = "<0..23>:<0..59> | <0..24000>",
-       description = "Set time of day",
+       params = "[<0..23>:<0..59> | <0..24000>]",
+       description = "Show or set time of day",
        privs = {},
        func = function(name, param)
                if param == "" then
        privs = {},
        func = function(name, param)
                if param == "" then
@@ -801,32 +851,45 @@ core.register_chatcommand("time", {
 })
 
 core.register_chatcommand("days", {
 })
 
 core.register_chatcommand("days", {
-       description = "Display day count",
+       description = "Show day count since world creation",
        func = function(name, param)
                return true, "Current day is " .. core.get_day_count()
        end
 })
 
 core.register_chatcommand("shutdown", {
        func = function(name, param)
                return true, "Current day is " .. core.get_day_count()
        end
 })
 
 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)
        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("([^ ]+)(.*)")
-               message = message or ""
-               core.request_shutdown(message:trim(), core.is_yes(reconnect))
+               local delay, reconnect, message
+               delay, param = param:match("^%s*(%S+)(.*)")
+               if param then
+                       reconnect, param = param:match("^%s*(%S+)(.*)")
+               end
+               message = param and param:match("^%s*(.+)") or ""
+               delay = tonumber(delay) or 0
+
+               if delay == 0 then
+                       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,
 })
 
 core.register_chatcommand("ban", {
        end,
 })
 
 core.register_chatcommand("ban", {
-       params = "<name>",
-       description = "Ban IP of player",
+       params = "[<name> | <IP_address>]",
+       description = "Ban player or show ban list",
        privs = {ban=true},
        func = function(name, param)
                if param == "" then
        privs = {ban=true},
        func = function(name, param)
                if param == "" then
-                       return true, "Ban list: " .. core.get_ban_list()
+                       local ban_list = core.get_ban_list()
+                       if ban_list == "" then
+                               return true, "The ban list is empty."
+                       else
+                               return true, "Ban list: " .. ban_list
+                       end
                end
                if not core.get_player_by_name(param) then
                        return false, "No such player."
                end
                if not core.get_player_by_name(param) then
                        return false, "No such player."
@@ -841,8 +904,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 player 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
@@ -854,7 +917,7 @@ core.register_chatcommand("unban", {
 })
 
 core.register_chatcommand("kick", {
 })
 
 core.register_chatcommand("kick", {
-       params = "<name> [reason]",
+       params = "<name> [<reason>]",
        description = "Kick a player",
        privs = {kick=true},
        func = function(name, param)
        description = "Kick a player",
        privs = {kick=true},
        func = function(name, param)
@@ -873,15 +936,15 @@ core.register_chatcommand("kick", {
 })
 
 core.register_chatcommand("clearobjects", {
 })
 
 core.register_chatcommand("clearobjects", {
-       params = "[full|quick]",
+       params = "[full | quick]",
        description = "Clear all objects in world",
        privs = {server=true},
        func = function(name, param)
                local options = {}
        description = "Clear all objects in world",
        privs = {server=true},
        func = function(name, param)
                local options = {}
-               if param == "" or param == "full" then
-                       options.mode = "full"
-               elseif param == "quick" then
+               if param == "" or param == "quick" then
                        options.mode = "quick"
                        options.mode = "quick"
+               elseif param == "full" then
+                       options.mode = "full"
                else
                        return false, "Invalid usage, see /help clearobjects."
                end
                else
                        return false, "Invalid usage, see /help clearobjects."
                end
@@ -919,8 +982,8 @@ core.register_chatcommand("msg", {
 })
 
 core.register_chatcommand("last-login", {
 })
 
 core.register_chatcommand("last-login", {
-       params = "[name]",
-       description = "Get the last login time of a player",
+       params = "[<name>]",
+       description = "Get the last login time of a player or yourself",
        func = function(name, param)
                if param == "" then
                        param = name
        func = function(name, param)
                if param == "" then
                        param = name
@@ -934,3 +997,62 @@ 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 clear another player's inventory (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,
+})
+
+local function handle_kill_command(killer, victim)
+       if core.settings:get_bool("enable_damage") == false then
+               return false, "Players can't be killed, damage has been disabled."
+       end
+       local victimref = core.get_player_by_name(victim)
+       if victimref == nil then
+               return false, string.format("Player %s is not online.", victim)
+       elseif victimref:get_hp() <= 0 then
+               if killer == victim then
+                       return false, "You are already dead."
+               else
+                       return false, string.format("%s is already dead.", victim)
+               end
+       end
+       if not killer == victim then
+               core.log("action", string.format("%s killed %s", killer, victim))
+       end
+       -- Kill victim
+       victimref:set_hp(0)
+       return true, string.format("%s has been killed.", victim)
+end
+
+core.register_chatcommand("kill", {
+       params = "[<name>]",
+       description = "Kill player or yourself",
+       privs = {server=true},
+       func = function(name, param)
+               return handle_kill_command(name, param == "" and name or param)
+       end,
+})