225 lines
7.8 KiB
Lua
225 lines
7.8 KiB
Lua
local S = minetest.get_translator("cmdtool")
|
|
local F = minetest.formspec_escape
|
|
|
|
local NEWLINE = "\n"
|
|
local DESCRIPTION = S("Command Tool")
|
|
local MAX_CMD_TOOLTIP_LEN = 48
|
|
|
|
local print_result = minetest.settings:get_bool("cmdtool_print_result", true)
|
|
|
|
local split_commands = function(commands_string)
|
|
return string.split(commands_string, NEWLINE)
|
|
end
|
|
|
|
local set_commands = function(itemstack, commands_string)
|
|
local meta = itemstack:get_meta()
|
|
meta:set_string("cmd", commands_string)
|
|
local cmds = split_commands(commands_string)
|
|
local first_cmd
|
|
if #cmds >= 1 then
|
|
first_cmd = cmds[1]
|
|
if string.len(first_cmd) > MAX_CMD_TOOLTIP_LEN then
|
|
first_cmd = string.sub(first_cmd, 1, MAX_CMD_TOOLTIP_LEN) .. " (…)"
|
|
end
|
|
local tooltip = DESCRIPTION .. NEWLINE .. first_cmd
|
|
if #cmds == 2 then
|
|
tooltip = tooltip .. NEWLINE .. S("… and 1 more command")
|
|
elseif #cmds > 2 then
|
|
tooltip = tooltip .. NEWLINE .. S("… and @1 more command(s)", #cmds - 1)
|
|
end
|
|
meta:set_string("description", tooltip)
|
|
else
|
|
meta:set_string("description", "")
|
|
end
|
|
return itemstack
|
|
end
|
|
|
|
-- Returns a table of commands in a command tool itemstack
|
|
local get_commands = function(itemstack)
|
|
local meta = itemstack:get_meta()
|
|
local cmd_str = meta:get_string("cmd")
|
|
local cmds = split_commands(cmd_str)
|
|
return cmds
|
|
end
|
|
|
|
--[[ Takes a command and substitutes placeholders like “@playername” with the actual values.
|
|
This operation might fail.
|
|
Returns the substituted command on success.
|
|
Returns false on failure. ]]
|
|
local substitute_placeholders = function(command, itemstack, player, pointed_thing)
|
|
local pos_pt, pos_pl
|
|
pos_pl = player:get_pos()
|
|
if pointed_thing.type == "node" then
|
|
pos_pt = pointed_thing.under
|
|
|
|
command = string.gsub(command, "@ptx", pos_pt.x)
|
|
command = string.gsub(command, "@pty", pos_pt.y)
|
|
command = string.gsub(command, "@ptz", pos_pt.z)
|
|
|
|
local node = minetest.get_node(pos_pt)
|
|
command = string.gsub(command, "@nodename", node.name)
|
|
command = string.gsub(command, "@param2", node.param2)
|
|
else
|
|
-- If one of the coordinate parameters is in the command, but
|
|
-- the pointed thing is not a node, we have to fail as the placeholders
|
|
-- are meaningless.
|
|
local coord = {"@ptx", "@pty", "@ptz", "@nodename", "@param2", "@light"}
|
|
for c=1, #coord do
|
|
if string.find(command, coord[c]) ~= nil then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
command = string.gsub(command, "@plx", pos_pl.x)
|
|
command = string.gsub(command, "@ply", pos_pl.y)
|
|
command = string.gsub(command, "@plz", pos_pl.z)
|
|
|
|
command = string.gsub(command, "@playername", player:get_player_name())
|
|
|
|
command = string.gsub(command, "@@", "@")
|
|
|
|
return command
|
|
end
|
|
|
|
local execute_command = function(itemstack, player, pointed_thing)
|
|
local player_name = player:get_player_name()
|
|
local cmds = get_commands(itemstack)
|
|
if not cmds then
|
|
return
|
|
end
|
|
local player_privs = minetest.get_player_privs(player_name)
|
|
for c=1, #cmds do
|
|
local cmd = cmds[1]
|
|
-- Substitution successful?
|
|
-- Split command string into command name and parameters
|
|
local cmd_split = string.split(cmd, " ", false, 1)
|
|
local cmd_name
|
|
-- Perform some checks:
|
|
-- 1. Command exists
|
|
-- 2. Player has all required privileges
|
|
if cmd_split then
|
|
-- Get command name
|
|
cmd_name = cmd_split[1]
|
|
local cmd_params = ""
|
|
if cmd_split[2] then
|
|
cmd_params = cmd_split[2]
|
|
end
|
|
cmd_params = substitute_placeholders(cmd_params, itemstack, player, pointed_thing)
|
|
local def = minetest.registered_chatcommands[cmd_name]
|
|
-- Command exists? Placeholder substitution successful?
|
|
if def then
|
|
if cmd_params then
|
|
local required_privs = def.privs
|
|
for priv, _ in pairs(required_privs) do
|
|
if player_privs[priv] ~= true then
|
|
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Insufficient privileges for command “@1”! You need the “@2” privilege.", cmd_name, priv)))
|
|
return
|
|
end
|
|
end
|
|
-- All tests survived!
|
|
-- Call the command
|
|
local retval, msg = def.func(player_name, cmd_params)
|
|
|
|
-- Print return value and message
|
|
if print_result then
|
|
if retval == true then
|
|
-- Command successfull
|
|
minetest.chat_send_player(player_name, minetest.colorize("#00FF00", "["..S("OK").."] ".. cmd))
|
|
elseif retval == false then
|
|
-- Command failed
|
|
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", "["..S("FAIL").."] ".. cmd))
|
|
elseif retval == nil then
|
|
-- Command result unknown
|
|
minetest.chat_send_player(player_name, minetest.colorize("#FF8800", "["..S("UNKN").."] ".. cmd))
|
|
end
|
|
end
|
|
if msg ~= nil and msg ~= "" then
|
|
local out = msg
|
|
if print_result then
|
|
local out = "> " .. msg
|
|
end
|
|
minetest.chat_send_player(player_name, out)
|
|
end
|
|
else
|
|
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Nothing pointed!")))
|
|
end
|
|
else
|
|
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("The command “@1” does not exist!", cmd_name)))
|
|
return
|
|
end
|
|
else
|
|
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Invalid command!")))
|
|
return
|
|
end
|
|
-- One iteration is done. We continue with the next command.
|
|
end
|
|
end
|
|
|
|
local open_command_configuration = function(itemstack, player, pointed_thing)
|
|
local player_name = player:get_player_name()
|
|
local commands = get_commands(itemstack)
|
|
local commands_str = ""
|
|
if commands then
|
|
for c=1, #commands do
|
|
commands_str = commands_str .. commands[c]
|
|
if c < #commands then
|
|
commands_str = commands_str .. "\n"
|
|
end
|
|
end
|
|
end
|
|
local formspec =
|
|
"size[12,6]"..
|
|
"textarea[0.25,0.25;12,5;commands;"..F(S("Commands:"))..";"..F(commands_str).."]"..
|
|
"button_exit[0.5,5;2,1;ok;"..F(S("OK")).."]"..
|
|
"button_exit[3.5,5;2,1;cancel;"..F(S("Cancel")).."]"
|
|
minetest.show_formspec(player_name, "cmdtool", formspec)
|
|
end
|
|
|
|
minetest.register_tool("cmdtool:cmdtool", {
|
|
description = DESCRIPTION,
|
|
_doc_items_longdesc = S("This is a programmable tool which can be used to run server commands."),
|
|
_doc_items_usagehelp = S("This tool is very mighty, so handle with care!").."\n"..
|
|
S("Use the [Place] key to set the commands. Write a list of server commands you wish to execute (with one command per line), in that order, but without the trailing slash like in the chat. Confirm with the OK button.").."\n"..
|
|
S("To run the commands, use the attack key. Note that commands might fail if you lack the required privileges or you made a mistake.").."\n\n"..
|
|
|
|
S("Optionally, you can use the following placeholders to insert variable values into your commands:").."\n"..
|
|
S("• @playername: Your player name").."\n"..
|
|
S("• @plx, @ply and @plz: Your player coordinates").."\n"..
|
|
S("• @@: Literal at sign").."\n\n"..
|
|
|
|
S("These placeholders only work when you use the tool on a block.").."\n"..
|
|
S("• @ptx, @pty and @ptz: Coordinates of the pointed node (i.e. block)").."\n"..
|
|
S("• @nodename: Itemstring of the pointed node").."\n"..
|
|
S("• @param2: param2 of the pointed node").."\n\n"..
|
|
|
|
S("Refer to “Advancd usage > Server commands” to learn more about server commands.").."\n\n\n"..
|
|
|
|
|
|
S([[Example 1:
|
|
time 12000
|
|
→ Sets time to midday.]]).."\n\n"..
|
|
|
|
S([[Example 2:
|
|
teleport @plx 9 @plz
|
|
giveme default:apple
|
|
→ Teleports you to Y=9 without changing the X and Z coordinates, then gives you an apple.]]),
|
|
inventory_image = "cmdtool_cmdtool.png",
|
|
wield_imagee = "cmdtool_cmdtool.png",
|
|
groups = { disable_repair = 1 },
|
|
on_use = execute_command,
|
|
on_place = open_command_configuration,
|
|
on_secondary_use = open_command_configuration,
|
|
})
|
|
|
|
-- Set commands
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
if formname == "cmdtool" and fields.ok and fields.commands ~= nil then
|
|
local wield_tool = player:get_wielded_item()
|
|
if wield_tool:get_name() == "cmdtool:cmdtool" then
|
|
local updated_tool = set_commands(wield_tool, fields.commands)
|
|
player:set_wielded_item(updated_tool)
|
|
end
|
|
end
|
|
end)
|