1 -- Minetest: builtin/game/chatcommands.lua
4 -- Chat command handler
7 core.chatcommands = core.registered_chatcommands -- BACKWARDS COMPATIBILITY
9 core.register_on_chat_message(function(name, message)
10 if message:sub(1,1) ~= "/" then
14 local cmd, param = string.match(message, "^/([^ ]+) *(.*)")
16 core.chat_send_player(name, "-!- Empty command")
22 local cmd_def = core.registered_chatcommands[cmd]
24 core.chat_send_player(name, "-!- Invalid command: " .. cmd)
27 local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs)
29 core.set_last_run_mod(cmd_def.mod_origin)
30 local success, message = cmd_def.func(name, param)
32 core.chat_send_player(name, message)
35 core.chat_send_player(name, "You don't have permission"
36 .. " to run this command (missing privileges: "
37 .. table.concat(missing_privs, ", ") .. ")")
39 return true -- Handled chat message
42 if core.settings:get_bool("profiler.load") then
43 -- Run after register_chatcommand and its register_on_chat_message
44 -- Before any chattcommands that should be profiled
45 profiler.init_chatcommand()
48 -- Parses a "range" string in the format of "here (number)" or
49 -- "(x1, y1, z1) (x2, y2, z2)", returning two position vectors
50 local function parse_range_str(player_name, str)
52 local args = str:split(" ")
54 if args[1] == "here" then
55 p1, p2 = core.get_player_radius_area(player_name, tonumber(args[2]))
57 return false, "Unable to get player " .. player_name .. " position"
60 p1, p2 = core.string_to_area(str)
62 return false, "Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)"
72 core.register_chatcommand("me", {
74 description = "Display chat action (e.g., '/me orders a pizza' displays"
75 .. " '<player name> orders a pizza')",
77 func = function(name, param)
78 core.chat_send_all("* " .. name .. " " .. param)
82 core.register_chatcommand("admin", {
83 description = "Show the name of the server owner",
85 local admin = minetest.settings:get("name")
87 return true, "The administrator of this server is "..admin.."."
89 return false, "There's no administrator named in the config file."
94 core.register_chatcommand("privs", {
96 description = "Print privileges of player",
97 func = function(caller, param)
99 local name = (param ~= "" and param or caller)
100 return true, "Privileges of " .. name .. ": "
101 .. core.privs_to_string(
102 core.get_player_privs(name), ' ')
106 local function handle_grant_command(caller, grantname, grantprivstr)
107 local caller_privs = minetest.get_player_privs(caller)
108 if not (caller_privs.privs or caller_privs.basic_privs) then
109 return false, "Your privileges are insufficient."
112 if not core.get_auth_handler().get_auth(grantname) then
113 return false, "Player " .. grantname .. " does not exist."
115 local grantprivs = core.string_to_privs(grantprivstr)
116 if grantprivstr == "all" then
117 grantprivs = core.registered_privileges
119 local privs = core.get_player_privs(grantname)
120 local privs_unknown = ""
122 core.string_to_privs(core.settings:get("basic_privs") or "interact,shout")
123 for priv, _ in pairs(grantprivs) do
124 if not basic_privs[priv] and not caller_privs.privs then
125 return false, "Your privileges are insufficient."
127 if not core.registered_privileges[priv] then
128 privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n"
132 if privs_unknown ~= "" then
133 return false, privs_unknown
135 for priv, _ in pairs(grantprivs) do
136 core.run_priv_callbacks(grantname, priv, caller, "grant")
138 core.set_player_privs(grantname, privs)
139 core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
140 if grantname ~= caller then
141 core.chat_send_player(grantname, caller
142 .. " granted you privileges: "
143 .. core.privs_to_string(grantprivs, ' '))
145 return true, "Privileges of " .. grantname .. ": "
146 .. core.privs_to_string(
147 core.get_player_privs(grantname), ' ')
150 core.register_chatcommand("grant", {
151 params = "<name> (<privilege> | all)",
152 description = "Give privilege to player",
153 func = function(name, param)
154 local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
155 if not grantname or not grantprivstr then
156 return false, "Invalid parameters (see /help grant)"
158 return handle_grant_command(name, grantname, grantprivstr)
162 core.register_chatcommand("grantme", {
163 params = "<privilege> | all",
164 description = "Grant privileges to yourself",
165 func = function(name, param)
167 return false, "Invalid parameters (see /help grantme)"
169 return handle_grant_command(name, name, param)
173 core.register_chatcommand("revoke", {
174 params = "<name> (<privilege> | all)",
175 description = "Remove privilege from player",
177 func = function(name, param)
178 if not core.check_player_privs(name, {privs=true}) and
179 not core.check_player_privs(name, {basic_privs=true}) then
180 return false, "Your privileges are insufficient."
182 local revoke_name, revoke_priv_str = string.match(param, "([^ ]+) (.+)")
183 if not revoke_name or not revoke_priv_str then
184 return false, "Invalid parameters (see /help revoke)"
185 elseif not core.get_auth_handler().get_auth(revoke_name) then
186 return false, "Player " .. revoke_name .. " does not exist."
188 local revoke_privs = core.string_to_privs(revoke_priv_str)
189 local privs = core.get_player_privs(revoke_name)
191 core.string_to_privs(core.settings:get("basic_privs") or "interact,shout")
192 for priv, _ in pairs(revoke_privs) do
193 if not basic_privs[priv] and
194 not core.check_player_privs(name, {privs=true}) then
195 return false, "Your privileges are insufficient."
198 if revoke_priv_str == "all" then
202 for priv, _ in pairs(revoke_privs) do
207 for priv, _ in pairs(revoke_privs) do
208 core.run_priv_callbacks(revoke_name, priv, name, "revoke")
211 core.set_player_privs(revoke_name, privs)
212 core.log("action", name..' revoked ('
213 ..core.privs_to_string(revoke_privs, ', ')
214 ..') privileges from '..revoke_name)
215 if revoke_name ~= name then
216 core.chat_send_player(revoke_name, name
217 .. " revoked privileges from you: "
218 .. core.privs_to_string(revoke_privs, ' '))
220 return true, "Privileges of " .. revoke_name .. ": "
221 .. core.privs_to_string(
222 core.get_player_privs(revoke_name), ' ')
226 core.register_chatcommand("setpassword", {
227 params = "<name> <password>",
228 description = "Set player's password",
229 privs = {password=true},
230 func = function(name, param)
231 local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$")
233 toname = param:match("^([^ ]+) *$")
237 return false, "Name field required"
239 local act_str_past = "?"
240 local act_str_pres = "?"
241 if not raw_password then
242 core.set_player_password(toname, "")
243 act_str_past = "cleared"
244 act_str_pres = "clears"
246 core.set_player_password(toname,
247 core.get_password_hash(toname,
250 act_str_pres = "sets"
252 if toname ~= name then
253 core.chat_send_player(toname, "Your password was "
254 .. act_str_past .. " by " .. name)
257 core.log("action", name .. " " .. act_str_pres
258 .. " password of " .. toname .. ".")
260 return true, "Password of player \"" .. toname .. "\" " .. act_str_past
264 core.register_chatcommand("clearpassword", {
266 description = "Set empty password",
267 privs = {password=true},
268 func = function(name, param)
271 return false, "Name field required"
273 core.set_player_password(toname, '')
275 core.log("action", name .. " clears password of " .. toname .. ".")
277 return true, "Password of player \"" .. toname .. "\" cleared"
281 core.register_chatcommand("auth_reload", {
283 description = "Reload authentication data",
284 privs = {server=true},
285 func = function(name, param)
286 local done = core.auth_reload()
287 return done, (done and "Done." or "Failed.")
291 core.register_chatcommand("remove_player", {
293 description = "Remove player data",
294 privs = {server=true},
295 func = function(name, param)
298 return false, "Name field required"
301 local rc = core.remove_player(toname)
304 core.log("action", name .. " removed player data of " .. toname .. ".")
305 return true, "Player \"" .. toname .. "\" removed."
307 return true, "No such player \"" .. toname .. "\" to remove."
309 return true, "Player \"" .. toname .. "\" is connected, cannot remove."
312 return false, "Unhandled remove_player return code " .. rc .. ""
316 core.register_chatcommand("teleport", {
317 params = "<X>,<Y>,<Z> | <to_name> | (<name> <X>,<Y>,<Z>) | (<name> <to_name>)",
318 description = "Teleport to player or position",
319 privs = {teleport=true},
320 func = function(name, param)
321 -- Returns (pos, true) if found, otherwise (pos, false)
322 local function find_free_position_near(pos)
329 for _, d in ipairs(tries) do
330 local p = {x = pos.x+d.x, y = pos.y+d.y, z = pos.z+d.z}
331 local n = core.get_node_or_nil(p)
333 local def = core.registered_nodes[n.name]
334 if def and not def.walkable then
342 local teleportee = nil
344 p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
348 if p.x and p.y and p.z then
350 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
351 return false, "Cannot teleport out of map bounds!"
353 teleportee = core.get_player_by_name(name)
356 return true, "Teleporting to "..core.pos_to_string(p)
360 local teleportee = nil
362 local target_name = nil
363 target_name = param:match("^([^ ]+)$")
364 teleportee = core.get_player_by_name(name)
366 local target = core.get_player_by_name(target_name)
371 if teleportee and p then
372 p = find_free_position_near(p)
374 return true, "Teleporting to " .. target_name
375 .. " at "..core.pos_to_string(p)
378 if not core.check_player_privs(name, {bring=true}) then
379 return false, "You don't have permission to teleport other players (missing bring privilege)"
382 local teleportee = nil
384 local teleportee_name = nil
385 teleportee_name, p.x, p.y, p.z = param:match(
386 "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
387 p.x, p.y, p.z = tonumber(p.x), tonumber(p.y), tonumber(p.z)
388 if teleportee_name then
389 teleportee = core.get_player_by_name(teleportee_name)
391 if teleportee and p.x and p.y and p.z then
393 return true, "Teleporting " .. teleportee_name
394 .. " to " .. core.pos_to_string(p)
397 local teleportee = nil
399 local teleportee_name = nil
400 local target_name = nil
401 teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$")
402 if teleportee_name then
403 teleportee = core.get_player_by_name(teleportee_name)
406 local target = core.get_player_by_name(target_name)
411 if teleportee and p then
412 p = find_free_position_near(p)
414 return true, "Teleporting " .. teleportee_name
415 .. " to " .. target_name
416 .. " at " .. core.pos_to_string(p)
419 return false, 'Invalid parameters ("' .. param
420 .. '") or player not found (see /help teleport)'
424 core.register_chatcommand("set", {
425 params = "([-n] <name> <value>) | <name>",
426 description = "Set or read server configuration setting",
427 privs = {server=true},
428 func = function(name, param)
429 local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)")
430 if arg and arg == "-n" and setname and setvalue then
431 core.settings:set(setname, setvalue)
432 return true, setname .. " = " .. setvalue
434 local setname, setvalue = string.match(param, "([^ ]+) (.+)")
435 if setname and setvalue then
436 if not core.settings:get(setname) then
437 return false, "Failed. Use '/set -n <name> <value>' to create a new setting."
439 core.settings:set(setname, setvalue)
440 return true, setname .. " = " .. setvalue
442 local setname = string.match(param, "([^ ]+)")
444 local setvalue = core.settings:get(setname)
446 setvalue = "<not set>"
448 return true, setname .. " = " .. setvalue
450 return false, "Invalid parameters (see /help set)."
454 local function emergeblocks_callback(pos, action, num_calls_remaining, ctx)
455 if ctx.total_blocks == 0 then
456 ctx.total_blocks = num_calls_remaining + 1
457 ctx.current_blocks = 0
459 ctx.current_blocks = ctx.current_blocks + 1
461 if ctx.current_blocks == ctx.total_blocks then
462 core.chat_send_player(ctx.requestor_name,
463 string.format("Finished emerging %d blocks in %.2fms.",
464 ctx.total_blocks, (os.clock() - ctx.start_time) * 1000))
468 local function emergeblocks_progress_update(ctx)
469 if ctx.current_blocks ~= ctx.total_blocks then
470 core.chat_send_player(ctx.requestor_name,
471 string.format("emergeblocks update: %d/%d blocks emerged (%.1f%%)",
472 ctx.current_blocks, ctx.total_blocks,
473 (ctx.current_blocks / ctx.total_blocks) * 100))
475 core.after(2, emergeblocks_progress_update, ctx)
479 core.register_chatcommand("emergeblocks", {
480 params = "(here [<radius>]) | (<pos1> <pos2>)",
481 description = "Load (or, if nonexistent, generate) map blocks "
482 .. "contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)",
483 privs = {server=true},
484 func = function(name, param)
485 local p1, p2 = parse_range_str(name, param)
493 start_time = os.clock(),
494 requestor_name = name
497 core.emerge_area(p1, p2, emergeblocks_callback, context)
498 core.after(2, emergeblocks_progress_update, context)
500 return true, "Started emerge of area ranging from " ..
501 core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1)
505 core.register_chatcommand("deleteblocks", {
506 params = "(here [<radius>]) | (<pos1> <pos2>)",
507 description = "Delete map blocks contained in area pos1 to pos2 "
508 .. "(<pos1> and <pos2> must be in parentheses)",
509 privs = {server=true},
510 func = function(name, param)
511 local p1, p2 = parse_range_str(name, param)
516 if core.delete_area(p1, p2) then
517 return true, "Successfully cleared area ranging from " ..
518 core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1)
520 return false, "Failed to clear one or more blocks in area"
525 core.register_chatcommand("fixlight", {
526 params = "(here [<radius>]) | (<pos1> <pos2>)",
527 description = "Resets lighting in the area between pos1 and pos2 "
528 .. "(<pos1> and <pos2> must be in parentheses)",
529 privs = {server = true},
530 func = function(name, param)
531 local p1, p2 = parse_range_str(name, param)
536 if core.fix_light(p1, p2) then
537 return true, "Successfully reset light in the area ranging from " ..
538 core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1)
540 return false, "Failed to load one or more blocks in area"
545 core.register_chatcommand("mods", {
547 description = "List mods installed on the server",
549 func = function(name, param)
550 return true, table.concat(core.get_modnames(), ", ")
554 local function handle_give_command(cmd, giver, receiver, stackstring)
555 core.log("action", giver .. " invoked " .. cmd
556 .. ', stackstring="' .. stackstring .. '"')
557 local itemstack = ItemStack(stackstring)
558 if itemstack:is_empty() then
559 return false, "Cannot give an empty item"
560 elseif not itemstack:is_known() then
561 return false, "Cannot give an unknown item"
563 local receiverref = core.get_player_by_name(receiver)
564 if receiverref == nil then
565 return false, receiver .. " is not a known player"
567 local leftover = receiverref:get_inventory():add_item("main", itemstack)
569 if leftover:is_empty() then
571 elseif leftover:get_count() == itemstack:get_count() then
572 partiality = "could not be "
574 partiality = "partially "
576 -- The actual item stack string may be different from what the "giver"
577 -- entered (e.g. big numbers are always interpreted as 2^16-1).
578 stackstring = itemstack:to_string()
579 if giver == receiver then
580 return true, ("%q %sadded to inventory.")
581 :format(stackstring, partiality)
583 core.chat_send_player(receiver, ("%q %sadded to inventory.")
584 :format(stackstring, partiality))
585 return true, ("%q %sadded to %s's inventory.")
586 :format(stackstring, partiality, receiver)
590 core.register_chatcommand("give", {
591 params = "<name> <ItemString>",
592 description = "Give item to player",
594 func = function(name, param)
595 local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$")
596 if not toname or not itemstring then
597 return false, "Name and ItemString required"
599 return handle_give_command("/give", name, toname, itemstring)
603 core.register_chatcommand("giveme", {
604 params = "<ItemString>",
605 description = "Give item to yourself",
607 func = function(name, param)
608 local itemstring = string.match(param, "(.+)$")
609 if not itemstring then
610 return false, "ItemString required"
612 return handle_give_command("/giveme", name, name, itemstring)
616 core.register_chatcommand("spawnentity", {
617 params = "<EntityName> [<X>,<Y>,<Z>]",
618 description = "Spawn entity at given (or your) position",
619 privs = {give=true, interact=true},
620 func = function(name, param)
621 local entityname, p = string.match(param, "^([^ ]+) *(.*)$")
622 if not entityname then
623 return false, "EntityName required"
625 core.log("action", ("%s invokes /spawnentity, entityname=%q")
626 :format(name, entityname))
627 local player = core.get_player_by_name(name)
628 if player == nil then
629 core.log("error", "Unable to spawn entity, player is nil")
630 return false, "Unable to spawn entity, player is nil"
635 p = core.string_to_pos(p)
637 return false, "Invalid parameters ('" .. param .. "')"
641 core.add_entity(p, entityname)
642 return true, ("%q spawned."):format(entityname)
646 core.register_chatcommand("pulverize", {
648 description = "Destroy item in hand",
649 func = function(name, param)
650 local player = core.get_player_by_name(name)
652 core.log("error", "Unable to pulverize, no player.")
653 return false, "Unable to pulverize, no player."
655 if player:get_wielded_item():is_empty() then
656 return false, "Unable to pulverize, no item in hand."
658 player:set_wielded_item(nil)
659 return true, "An item was pulverized."
664 core.rollback_punch_callbacks = {}
666 core.register_on_punchnode(function(pos, node, puncher)
667 local name = puncher:get_player_name()
668 if core.rollback_punch_callbacks[name] then
669 core.rollback_punch_callbacks[name](pos, node, puncher)
670 core.rollback_punch_callbacks[name] = nil
674 core.register_chatcommand("rollback_check", {
675 params = "[<range>] [<seconds>] [<limit>]",
676 description = "Check who last touched a node or a node near it"
677 .. " within the time specified by <seconds>. Default: range = 0,"
678 .. " seconds = 86400 = 24h, limit = 5",
679 privs = {rollback=true},
680 func = function(name, param)
681 if not core.settings:get_bool("enable_rollback_recording") then
682 return false, "Rollback functions are disabled."
684 local range, seconds, limit =
685 param:match("(%d+) *(%d*) *(%d*)")
686 range = tonumber(range) or 0
687 seconds = tonumber(seconds) or 86400
688 limit = tonumber(limit) or 5
690 return false, "That limit is too high!"
693 core.rollback_punch_callbacks[name] = function(pos, node, puncher)
694 local name = puncher:get_player_name()
695 core.chat_send_player(name, "Checking " .. core.pos_to_string(pos) .. "...")
696 local actions = core.rollback_get_node_actions(pos, range, seconds, limit)
698 core.chat_send_player(name, "Rollback functions are disabled")
701 local num_actions = #actions
702 if num_actions == 0 then
703 core.chat_send_player(name, "Nobody has touched"
704 .. " the specified location in "
705 .. seconds .. " seconds")
708 local time = os.time()
709 for i = num_actions, 1, -1 do
710 local action = actions[i]
711 core.chat_send_player(name,
712 ("%s %s %s -> %s %d seconds ago.")
714 core.pos_to_string(action.pos),
722 return true, "Punch a node (range=" .. range .. ", seconds="
723 .. seconds .. "s, limit=" .. limit .. ")"
727 core.register_chatcommand("rollback", {
728 params = "(<name> [<seconds>]) | (:<actor> [<seconds>])",
729 description = "Revert actions of a player. Default for <seconds> is 60",
730 privs = {rollback=true},
731 func = function(name, param)
732 if not core.settings:get_bool("enable_rollback_recording") then
733 return false, "Rollback functions are disabled."
735 local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
736 if not target_name then
737 local player_name = nil
738 player_name, seconds = string.match(param, "([^ ]+) *(%d*)")
739 if not player_name then
740 return false, "Invalid parameters. See /help rollback"
741 .. " and /help rollback_check."
743 target_name = "player:"..player_name
745 seconds = tonumber(seconds) or 60
746 core.chat_send_player(name, "Reverting actions of "
747 .. target_name .. " since "
748 .. seconds .. " seconds.")
749 local success, log = core.rollback_revert_actions_by(
750 target_name, seconds)
753 response = "(log is too long to show)\n"
755 for _, line in pairs(log) do
756 response = response .. line .. "\n"
759 response = response .. "Reverting actions "
760 .. (success and "succeeded." or "FAILED.")
761 return success, response
765 core.register_chatcommand("status", {
766 description = "Print server status",
767 func = function(name, param)
768 return true, core.get_server_status()
772 core.register_chatcommand("time", {
773 params = "<0..23>:<0..59> | <0..24000>",
774 description = "Set time of day",
776 func = function(name, param)
778 local current_time = math.floor(core.get_timeofday() * 1440)
779 local minutes = current_time % 60
780 local hour = (current_time - minutes) / 60
781 return true, ("Current time is %d:%02d"):format(hour, minutes)
783 local player_privs = core.get_player_privs(name)
784 if not player_privs.settime then
785 return false, "You don't have permission to run this command " ..
786 "(missing privilege: settime)."
788 local hour, minute = param:match("^(%d+):(%d+)$")
790 local new_time = tonumber(param)
792 return false, "Invalid time."
794 -- Backward compatibility.
795 core.set_timeofday((new_time % 24000) / 24000)
796 core.log("action", name .. " sets time to " .. new_time)
797 return true, "Time of day changed."
799 hour = tonumber(hour)
800 minute = tonumber(minute)
801 if hour < 0 or hour > 23 then
802 return false, "Invalid hour (must be between 0 and 23 inclusive)."
803 elseif minute < 0 or minute > 59 then
804 return false, "Invalid minute (must be between 0 and 59 inclusive)."
806 core.set_timeofday((hour * 60 + minute) / 1440)
807 core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute))
808 return true, "Time of day changed."
812 core.register_chatcommand("days", {
813 description = "Display day count",
814 func = function(name, param)
815 return true, "Current day is " .. core.get_day_count()
819 core.register_chatcommand("shutdown", {
820 params = "[<delay_in_seconds> | -1] [reconnect] [<message>]",
821 description = "Shutdown server (-1 cancels a delayed shutdown)",
822 privs = {server=true},
823 func = function(name, param)
824 local delay, reconnect, message = param:match("([^ ][-]?[0-9]+)([^ ]+)(.*)")
825 message = message or ""
828 delay = tonumber(param) or 0
831 core.log("action", name .. " shuts down server")
832 core.chat_send_all("*** Server shutting down (operator request).")
834 core.request_shutdown(message:trim(), core.is_yes(reconnect), delay)
838 core.register_chatcommand("ban", {
840 description = "Ban IP of player",
842 func = function(name, param)
844 return true, "Ban list: " .. core.get_ban_list()
846 if not core.get_player_by_name(param) then
847 return false, "No such player."
849 if not core.ban_player(param) then
850 return false, "Failed to ban player."
852 local desc = core.get_ban_description(param)
853 core.log("action", name .. " bans " .. desc .. ".")
854 return true, "Banned " .. desc .. "."
858 core.register_chatcommand("unban", {
859 params = "<name> | <IP_address>",
860 description = "Remove IP ban",
862 func = function(name, param)
863 if not core.unban_player_or_ip(param) then
864 return false, "Failed to unban player/IP."
866 core.log("action", name .. " unbans " .. param)
867 return true, "Unbanned " .. param
871 core.register_chatcommand("kick", {
872 params = "<name> [<reason>]",
873 description = "Kick a player",
875 func = function(name, param)
876 local tokick, reason = param:match("([^ ]+) (.+)")
877 tokick = tokick or param
878 if not core.kick_player(tokick, reason) then
879 return false, "Failed to kick player " .. tokick
881 local log_reason = ""
883 log_reason = " with reason \"" .. reason .. "\""
885 core.log("action", name .. " kicks " .. tokick .. log_reason)
886 return true, "Kicked " .. tokick
890 core.register_chatcommand("clearobjects", {
891 params = "[full | quick]",
892 description = "Clear all objects in world",
893 privs = {server=true},
894 func = function(name, param)
896 if param == "" or param == "full" then
897 options.mode = "full"
898 elseif param == "quick" then
899 options.mode = "quick"
901 return false, "Invalid usage, see /help clearobjects."
904 core.log("action", name .. " clears all objects ("
905 .. options.mode .. " mode).")
906 core.chat_send_all("Clearing all objects. This may take long."
907 .. " You may experience a timeout. (by "
909 core.clear_objects(options)
910 core.log("action", "Object clearing done.")
911 core.chat_send_all("*** Cleared all objects.")
915 core.register_chatcommand("msg", {
916 params = "<name> <message>",
917 description = "Send a private message",
918 privs = {shout=true},
919 func = function(name, param)
920 local sendto, message = param:match("^(%S+)%s(.+)$")
922 return false, "Invalid usage, see /help msg."
924 if not core.get_player_by_name(sendto) then
925 return false, "The player " .. sendto
928 core.log("action", "PM from " .. name .. " to " .. sendto
930 core.chat_send_player(sendto, "PM from " .. name .. ": "
932 return true, "Message sent."
936 core.register_chatcommand("last-login", {
938 description = "Get the last login time of a player",
939 func = function(name, param)
943 local pauth = core.get_auth_handler().get_auth(param)
944 if pauth and pauth.last_login then
945 -- Time in UTC, ISO 8601 format
946 return true, "Last login time was " ..
947 os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login)
949 return false, "Last login time is unknown"
953 core.register_chatcommand("clearinv", {
955 description = "Clear the inventory of yourself or another player",
956 func = function(name, param)
958 if param and param ~= "" and param ~= name then
959 if not core.check_player_privs(name, {server=true}) then
960 return false, "You don't have permission"
961 .. " to run this command (missing privilege: server)"
963 player = core.get_player_by_name(param)
964 core.chat_send_player(param, name.." cleared your inventory.")
966 player = core.get_player_by_name(name)
970 player:get_inventory():set_list("main", {})
971 player:get_inventory():set_list("craft", {})
972 player:get_inventory():set_list("craftpreview", {})
973 core.log("action", name.." clears "..player:get_player_name().."'s inventory")
974 return true, "Cleared "..player:get_player_name().."'s inventory."
976 return false, "Player must be online to clear inventory!"