1 local S
= minetest
.get_translator("cmdtool")
2 local F
= minetest
.formspec_escape
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
)
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
)
19 if string.len(first_cmd
) > MAX_CMD_TOOLTIP_LEN
then
20 first_cmd
= string.sub(first_cmd
, 1, MAX_CMD_TOOLTIP_LEN
) .. " (…)"
22 local tooltip
= DESCRIPTION
.. NEWLINE
.. first_cmd
24 tooltip
= tooltip
.. NEWLINE
.. S("… and 1 more command")
26 tooltip
= tooltip
.. NEWLINE
.. S("… and @1 more command(s)", #cmds
- 1)
28 meta
:set_string("description", tooltip
)
30 meta
:set_string("description", "")
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
)
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
)
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
)
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
64 local coord
= {"@ptx", "@pty", "@ptz", "@nodename", "@param2", "@light"}
66 if string.find(command
, coord
[c
]) ~= nil then
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
, "@@", "@")
83 local execute_command
= function(itemstack
, player
, pointed_thing
)
84 local player_name
= player
:get_player_name()
85 local cmds
= get_commands(itemstack
)
89 local player_privs
= minetest
.get_player_privs(player_name
)
92 -- Substitution successful?
93 -- Split command string into command name and parameters
94 local cmd_split
= string.split(cmd
, " ", false, 1)
96 -- Perform some checks:
98 -- 2. Player has all required privileges
101 cmd_name
= cmd_split
[1]
102 local cmd_params
= ""
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?
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
)))
118 -- All tests survived!
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
)
134 minetest
.chat_send_player(player_name
, minetest
.colorize("#FF0000", S("Nothing pointed!")))
137 minetest
.chat_send_player(player_name
, minetest
.colorize("#FF0000", S("The command “@1” does not exist!", cmd_name
)))
141 minetest
.chat_send_player(player_name
, minetest
.colorize("#FF0000", S("Invalid command!")))
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
= ""
153 for c
=1, #commands
do
154 commands_str
= commands_str
.. commands
[c
]
155 if c
< #commands
then
156 commands_str
= commands_str
.. "\n"
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"..
190 → Sets time to midday.]]).."\n\n"..
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
,
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
)