minetest_cmdtool/init.lua

225 lines
7.8 KiB
Lua
Raw Permalink Normal View History

2020-11-18 06:31:57 -08:00
local S = minetest.get_translator("cmdtool")
local F = minetest.formspec_escape
2018-01-16 11:47:53 -08:00
local NEWLINE = "\n"
2020-11-18 06:31:57 -08:00
local DESCRIPTION = S("Command Tool")
2018-01-16 12:19:59 -08:00
local MAX_CMD_TOOLTIP_LEN = 48
2020-11-18 06:53:55 -08:00
local print_result = minetest.settings:get_bool("cmdtool_print_result", true)
2018-01-16 12:19:59 -08:00
local split_commands = function(commands_string)
return string.split(commands_string, NEWLINE)
end
2018-01-16 11:47:53 -08:00
local set_commands = function(itemstack, commands_string)
local meta = itemstack:get_meta()
meta:set_string("cmd", commands_string)
2018-01-16 12:19:59 -08:00
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
2020-11-18 06:31:57 -08:00
tooltip = tooltip .. NEWLINE .. S("… and 1 more command")
2018-01-16 12:19:59 -08:00
elseif #cmds > 2 then
2020-11-18 06:31:57 -08:00
tooltip = tooltip .. NEWLINE .. S("… and @1 more command(s)", #cmds - 1)
2018-01-16 12:19:59 -08:00
end
meta:set_string("description", tooltip)
else
meta:set_string("description", "")
end
2018-01-16 11:47:53 -08:00
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")
2018-01-16 12:19:59 -08:00
local cmds = split_commands(cmd_str)
2018-01-16 11:47:53 -08:00
return cmds
end
2018-01-16 13:04:19 -08:00
--[[ 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
2018-01-16 11:47:53 -08:00
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[c]
2018-01-16 13:04:19 -08:00
-- Substitution successful?
2018-01-16 11:47:53 -08:00
-- 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
2018-01-16 13:04:19 -08:00
cmd_params = substitute_placeholders(cmd_params, itemstack, player, pointed_thing)
2018-01-16 11:47:53 -08:00
local def = minetest.registered_chatcommands[cmd_name]
2018-01-16 13:04:19 -08:00
-- Command exists? Placeholder substitution successful?
2018-01-16 11:47:53 -08:00
if def then
2018-01-16 13:04:19 -08:00
if cmd_params then
local required_privs = def.privs
for priv, _ in pairs(required_privs) do
if player_privs[priv] ~= true then
2020-11-18 06:31:57 -08:00
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Insufficient privileges for command “@1”! You need the “@2” privilege.", cmd_name, priv)))
2018-01-16 13:04:19 -08:00
return
end
2018-01-16 11:47:53 -08:00
end
2018-01-16 13:04:19 -08:00
-- All tests survived!
-- Call the command
2020-11-18 06:31:57 -08:00
local retval, msg = def.func(player_name, cmd_params)
-- Print return value and message
2020-11-18 06:53:55 -08:00
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
2020-11-18 06:31:57 -08:00
end
if msg ~= nil and msg ~= "" then
2020-11-18 06:53:55 -08:00
local out = msg
if print_result then
local out = "> " .. msg
end
minetest.chat_send_player(player_name, out)
2020-11-18 06:31:57 -08:00
end
2018-01-16 13:04:19 -08:00
else
2020-11-18 06:31:57 -08:00
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Nothing pointed!")))
2018-01-16 11:47:53 -08:00
end
else
2020-11-18 06:31:57 -08:00
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("The command “@1” does not exist!", cmd_name)))
2018-01-16 11:47:53 -08:00
return
end
else
2020-11-18 06:31:57 -08:00
minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Invalid command!")))
2018-01-16 11:47:53 -08:00
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 =
2020-11-18 06:39:09 -08:00
"size[12,6]"..
"textarea[0.25,0.25;12,5;commands;"..F(S("Commands:"))..";"..F(commands_str).."]"..
2020-11-18 06:31:57 -08:00
"button_exit[0.5,5;2,1;ok;"..F(S("OK")).."]"..
"button_exit[3.5,5;2,1;cancel;"..F(S("Cancel")).."]"
2018-01-16 11:47:53 -08:00
minetest.show_formspec(player_name, "cmdtool", formspec)
end
minetest.register_tool("cmdtool:cmdtool", {
2018-01-16 12:19:59 -08:00
description = DESCRIPTION,
2020-11-18 06:31:57 -08:00
_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"..
2020-11-18 06:33:03 -08:00
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"..
2020-11-18 06:31:57 -08:00
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"..
2018-01-16 13:25:17 -08:00
2020-11-18 06:31:57 -08:00
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"..
2018-01-16 13:25:17 -08:00
2020-11-18 06:31:57 -08:00
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"..
2018-01-16 13:25:17 -08:00
2020-11-18 06:31:57 -08:00
S("Refer to “Advancd usage > Server commands” to learn more about server commands.").."\n\n\n"..
2018-01-16 13:25:17 -08:00
2020-11-18 06:31:57 -08:00
S([[Example 1:
2018-01-16 13:25:17 -08:00
time 12000
2020-11-18 06:31:57 -08:00
Sets time to midday.]]).."\n\n"..
2018-01-16 13:25:17 -08:00
2020-11-18 06:31:57 -08:00
S([[Example 2:
2018-01-16 13:25:17 -08:00
teleport @plx 9 @plz
giveme default:apple
2020-11-18 06:31:57 -08:00
Teleports you to Y=9 without changing the X and Z coordinates, then gives you an apple.]]),
2018-01-16 11:47:53 -08:00
inventory_image = "cmdtool_cmdtool.png",
wield_imagee = "cmdtool_cmdtool.png",
2020-11-18 06:34:06 -08:00
groups = { disable_repair = 1 },
2018-01-16 12:05:18 -08:00
on_use = execute_command,
on_place = open_command_configuration,
on_secondary_use = open_command_configuration,
2018-01-16 11:47:53 -08:00
})
-- 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)