Enlarge formspec
[minetest_cmdtool.git] / init.lua
blobb9b11f57fcdfd6f38c4a93742c8e5881e8c39022
1 local S = minetest.get_translator("cmdtool")
2 local F = minetest.formspec_escape
4 local NEWLINE = "\n"
5 local DESCRIPTION = S("Command Tool")
6 local MAX_CMD_TOOLTIP_LEN = 48
8 local split_commands = function(commands_string)
9 return string.split(commands_string, NEWLINE)
10 end
12 local set_commands = function(itemstack, commands_string)
13 local meta = itemstack:get_meta()
14 meta:set_string("cmd", commands_string)
15 local cmds = split_commands(commands_string)
16 local first_cmd
17 if #cmds >= 1 then
18 first_cmd = cmds[1]
19 if string.len(first_cmd) > MAX_CMD_TOOLTIP_LEN then
20 first_cmd = string.sub(first_cmd, 1, MAX_CMD_TOOLTIP_LEN) .. " (…)"
21 end
22 local tooltip = DESCRIPTION .. NEWLINE .. first_cmd
23 if #cmds == 2 then
24 tooltip = tooltip .. NEWLINE .. S("… and 1 more command")
25 elseif #cmds > 2 then
26 tooltip = tooltip .. NEWLINE .. S("… and @1 more command(s)", #cmds - 1)
27 end
28 meta:set_string("description", tooltip)
29 else
30 meta:set_string("description", "")
31 end
32 return itemstack
33 end
35 -- Returns a table of commands in a command tool itemstack
36 local get_commands = function(itemstack)
37 local meta = itemstack:get_meta()
38 local cmd_str = meta:get_string("cmd")
39 local cmds = split_commands(cmd_str)
40 return cmds
41 end
43 --[[ Takes a command and substitutes placeholders like “@playername” with the actual values.
44 This operation might fail.
45 Returns the substituted command on success.
46 Returns false on failure. ]]
47 local substitute_placeholders = function(command, itemstack, player, pointed_thing)
48 local pos_pt, pos_pl
49 pos_pl = player:get_pos()
50 if pointed_thing.type == "node" then
51 pos_pt = pointed_thing.under
53 command = string.gsub(command, "@ptx", pos_pt.x)
54 command = string.gsub(command, "@pty", pos_pt.y)
55 command = string.gsub(command, "@ptz", pos_pt.z)
57 local node = minetest.get_node(pos_pt)
58 command = string.gsub(command, "@nodename", node.name)
59 command = string.gsub(command, "@param2", node.param2)
60 else
61 -- If one of the coordinate parameters is in the command, but
62 -- the pointed thing is not a node, we have to fail as the placeholders
63 -- are meaningless.
64 local coord = {"@ptx", "@pty", "@ptz", "@nodename", "@param2", "@light"}
65 for c=1, #coord do
66 if string.find(command, coord[c]) ~= nil then
67 return false
68 end
69 end
70 end
72 command = string.gsub(command, "@plx", pos_pl.x)
73 command = string.gsub(command, "@ply", pos_pl.y)
74 command = string.gsub(command, "@plz", pos_pl.z)
76 command = string.gsub(command, "@playername", player:get_player_name())
78 command = string.gsub(command, "@@", "@")
80 return command
81 end
83 local execute_command = function(itemstack, player, pointed_thing)
84 local player_name = player:get_player_name()
85 local cmds = get_commands(itemstack)
86 if not cmds then
87 return
88 end
89 local player_privs = minetest.get_player_privs(player_name)
90 for c=1, #cmds do
91 local cmd = cmds[1]
92 -- Substitution successful?
93 -- Split command string into command name and parameters
94 local cmd_split = string.split(cmd, " ", false, 1)
95 local cmd_name
96 -- Perform some checks:
97 -- 1. Command exists
98 -- 2. Player has all required privileges
99 if cmd_split then
100 -- Get command name
101 cmd_name = cmd_split[1]
102 local cmd_params = ""
103 if cmd_split[2] then
104 cmd_params = cmd_split[2]
106 cmd_params = substitute_placeholders(cmd_params, itemstack, player, pointed_thing)
107 local def = minetest.registered_chatcommands[cmd_name]
108 -- Command exists? Placeholder substitution successful?
109 if def then
110 if cmd_params then
111 local required_privs = def.privs
112 for priv, _ in pairs(required_privs) do
113 if player_privs[priv] ~= true then
114 minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Insufficient privileges for command “@1”! You need the “@2” privilege.", cmd_name, priv)))
115 return
118 -- All tests survived!
119 -- Call the command
120 local retval, msg = def.func(player_name, cmd_params)
122 -- Print return value and message
123 if retval == true then
124 minetest.chat_send_player(player_name, minetest.colorize("#00FF00", "["..S("OK").."] ".. cmd))
125 elseif retval == false then
126 minetest.chat_send_player(player_name, minetest.colorize("#FF0000", "["..S("FAIL").."] ".. cmd))
127 elseif retval == nil then
128 minetest.chat_send_player(player_name, minetest.colorize("#FF8800", "["..S("UNKN").."] ".. cmd))
130 if msg ~= nil and msg ~= "" then
131 minetest.chat_send_player(player_name, "> " .. msg)
133 else
134 minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Nothing pointed!")))
136 else
137 minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("The command “@1” does not exist!", cmd_name)))
138 return
140 else
141 minetest.chat_send_player(player_name, minetest.colorize("#FF0000", S("Invalid command!")))
142 return
144 -- One iteration is done. We continue with the next command.
148 local open_command_configuration = function(itemstack, player, pointed_thing)
149 local player_name = player:get_player_name()
150 local commands = get_commands(itemstack)
151 local commands_str = ""
152 if commands then
153 for c=1, #commands do
154 commands_str = commands_str .. commands[c]
155 if c < #commands then
156 commands_str = commands_str .. "\n"
160 local formspec =
161 "size[12,6]"..
162 "textarea[0.25,0.25;12,5;commands;"..F(S("Commands:"))..";"..F(commands_str).."]"..
163 "button_exit[0.5,5;2,1;ok;"..F(S("OK")).."]"..
164 "button_exit[3.5,5;2,1;cancel;"..F(S("Cancel")).."]"
165 minetest.show_formspec(player_name, "cmdtool", formspec)
168 minetest.register_tool("cmdtool:cmdtool", {
169 description = DESCRIPTION,
170 _doc_items_longdesc = S("This is a programmable tool which can be used to run server commands."),
171 _doc_items_usagehelp = S("This tool is very mighty, so handle with care!").."\n"..
172 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"..
173 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"..
175 S("Optionally, you can use the following placeholders to insert variable values into your commands:").."\n"..
176 S("• @playername: Your player name").."\n"..
177 S("• @plx, @ply and @plz: Your player coordinates").."\n"..
178 S("• @@: Literal at sign").."\n\n"..
180 S("These placeholders only work when you use the tool on a block.").."\n"..
181 S("• @ptx, @pty and @ptz: Coordinates of the pointed node (i.e. block)").."\n"..
182 S("• @nodename: Itemstring of the pointed node").."\n"..
183 S("• @param2: param2 of the pointed node").."\n\n"..
185 S("Refer to “Advancd usage > Server commands” to learn more about server commands.").."\n\n\n"..
188 S([[Example 1:
189 time 12000
190 → Sets time to midday.]]).."\n\n"..
192 S([[Example 2:
193 teleport @plx 9 @plz
194 giveme default:apple
195 → Teleports you to Y=9 without changing the X and Z coordinates, then gives you an apple.]]),
196 inventory_image = "cmdtool_cmdtool.png",
197 wield_imagee = "cmdtool_cmdtool.png",
198 groups = { disable_repair = 1 },
199 on_use = execute_command,
200 on_place = open_command_configuration,
201 on_secondary_use = open_command_configuration,
204 -- Set commands
205 minetest.register_on_player_receive_fields(function(player, formname, fields)
206 if formname == "cmdtool" and fields.ok and fields.commands ~= nil then
207 local wield_tool = player:get_wielded_item()
208 if wield_tool:get_name() == "cmdtool:cmdtool" then
209 local updated_tool = set_commands(wield_tool, fields.commands)
210 player:set_wielded_item(updated_tool)
213 end)