Make cmd block errors red
[MineClone/MineClone2.git] / mods / ITEMS / REDSTONE / mesecons_commandblock / init.lua
blob5255d2ceca67ee763856a89c86603a53928e54bf
2 local function construct(pos)
3 local meta = minetest.get_meta(pos)
5 meta:set_string("commands", "")
6 meta:set_string("commander", "")
7 end
9 local function after_place(pos, placer)
10 if placer then
11 local meta = minetest.get_meta(pos)
12 meta:set_string("commander", placer:get_player_name())
13 end
14 end
16 local function resolve_commands(commands, pos)
17 local players = minetest.get_connected_players()
19 local meta = minetest.get_meta(pos)
20 local commander = meta:get_string("commander")
22 -- A non-printable character used while replacing “@@”.
23 local SUBSTITUTE_CHARACTER = '\26' -- ASCII SUB
25 -- No players online: remove all commands containing
26 -- problematic placeholders.
27 if #players == 0 then
28 commands = commands:gsub("[^\r\n]+", function (line)
29 line = line:gsub("@@", SUBSTITUTE_CHARACTER)
30 if line:find("@n") then return "" end
31 if line:find("@p") then return "" end
32 if line:find("@f") then return "" end
33 if line:find("@r") then return "" end
34 line = line:gsub("@c", commander)
35 line = line:gsub(SUBSTITUTE_CHARACTER, "@")
36 return line
37 end)
38 return commands
39 end
41 local nearest, farthest = nil, nil
42 local min_distance, max_distance = math.huge, -1
43 for index, player in pairs(players) do
44 local distance = vector.distance(pos, player:getpos())
45 if distance < min_distance then
46 min_distance = distance
47 nearest = player:get_player_name()
48 end
49 if distance > max_distance then
50 max_distance = distance
51 farthest = player:get_player_name()
52 end
53 end
54 local random = players[math.random(#players)]:get_player_name()
55 commands = commands:gsub("@@", SUBSTITUTE_CHARACTER)
56 commands = commands:gsub("@p", nearest)
57 commands = commands:gsub("@n", nearest)
58 commands = commands:gsub("@f", farthest)
59 commands = commands:gsub("@r", random)
60 commands = commands:gsub("@c", commander)
61 commands = commands:gsub(SUBSTITUTE_CHARACTER, "@")
62 return commands
63 end
65 local function check_commands(commands, player_name)
66 for _, command in pairs(commands:split("\n")) do
67 local pos = command:find(" ")
68 local cmd, param = command, ""
69 if pos then
70 cmd = command:sub(1, pos - 1)
71 end
72 local cmddef = minetest.chatcommands[cmd]
73 if not cmddef then
74 -- Invalid chat command
75 local msg = "Error: The command “"..cmd.."” does not exist; your command block has not been changed. Use the “help” chat command for a list of available commands."
76 if string.sub(cmd, 1, 1) == "/" then
77 msg = msg .. " Hint: Try to remove the trailing slash."
78 end
79 return false, minetest.colorize("#FF0000", msg)
80 end
81 if player_name then
82 local player_privs = minetest.get_player_privs(player_name)
84 for cmd_priv, _ in pairs(cmddef.privs) do
85 if player_privs[cmd_priv] ~= true then
86 local msg = "Error: You have insufficient privileges to use the command “"..cmd.."” (missing privilege: "..cmd_priv..")! The command block has not been changed."
87 return false, minetest.colorize("#FF0000", msg)
88 end
89 end
90 end
91 end
92 return true
93 end
95 local function commandblock_action_on(pos, node)
96 if node.name ~= "mesecons_commandblock:commandblock_off" then
97 return
98 end
100 minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_on"})
102 local meta = minetest.get_meta(pos)
104 local commands = resolve_commands(meta:get_string("commands"), pos)
105 for _, command in pairs(commands:split("\n")) do
106 local cpos = command:find(" ")
107 local cmd, param = command, ""
108 if cpos then
109 cmd = command:sub(1, cpos - 1)
110 param = command:sub(cpos + 1)
112 local cmddef = minetest.chatcommands[cmd]
113 if not cmddef then
114 -- Invalid chat command
115 return
117 -- Execute command in the name of commander
118 local commander = meta:get_string("commander")
119 cmddef.func(commander, param)
123 local function commandblock_action_off(pos, node)
124 if node.name == "mesecons_commandblock:commandblock_on" then
125 minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_off"})
129 local on_rightclick = function(pos, node, player, itemstack, pointed_thing)
130 -- Only allow access in Creative Mode
131 if not minetest.settings:get_bool("creative_mode") then
132 return
134 local privs = minetest.get_player_privs(player:get_player_name())
135 if not privs.maphack then
136 minetest.chat_send_player(player:get_player_name(), "Access denied. You need the “maphack” privilege to edit command blocks.")
137 return
140 local meta = minetest.get_meta(pos)
141 local commands = meta:get_string("commands")
142 local commander = meta:get_string("commander")
143 local commanderstr
144 if commander == "" or commander == nil then
145 commanderstr = "Error: No commander! Block must be replaced."
146 else
147 commanderstr = "Commander: "..commander
149 local formspec = "invsize[9,5;]" ..
150 "textarea[0.5,0.5;8.5,4;commands;Commands;"..commands.."]" ..
151 "button_exit[3.3,4.5;2,1;submit;Submit]" ..
152 "image_button[8,4.5;1,1;doc_button_icon_lores.png;doc;]" ..
153 "label[0,4;"..minetest.formspec_escape(commanderstr).."]" ..
154 "tooltip[doc;Help]"
155 minetest.show_formspec(player:get_player_name(), "commandblock_"..pos.x.."_"..pos.y.."_"..pos.z, formspec)
158 local on_place = function(itemstack, placer, pointed_thing)
159 if pointed_thing.type ~= "node" then
160 return itemstack
163 -- Use pointed node's on_rightclick function first, if present
164 local node = minetest.get_node(pointed_thing.under)
165 if placer and not placer:get_player_control().sneak then
166 if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
167 return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
171 local privs = minetest.get_player_privs(placer:get_player_name())
172 if not privs.maphack then
173 minetest.chat_send_player(placer:get_player_name(), "Placement denied. You need the “maphack” privilege to place command blocks.")
174 return itemstack
177 return minetest.item_place_node(itemstack, placer, pointed_thing)
180 minetest.register_node("mesecons_commandblock:commandblock_off", {
181 description = "Command Block",
183 _doc_items_longdesc =
184 "Command blocks are mighty redstone components which are able to alter reality itself. In other words, they cause the server to execute server commands when they are supplied with redstone power.",
185 _doc_items_usagehelp =
186 [[To use an already existing command block, just supply it with redstone power and see what happens. This will execute the commands once. To execute the commands again, turn the redstone power off and on again.
188 To place a command block and change the commands, you need to be in Creative Mode and must have the “maphack” privilege. A new command block does not have any commands and does nothing. Rightclick the command block (in Creative Mode!) to edit its commands. Read the help entry “Advanced topics > Server Commands” to understand how they work. Each line contains a single command. You enter them like you would in the console, but without the leading slash. The commands will be executed from top to bottom.
190 All commands will be executed on behalf of the player who placed the command block, as if the player typed in the commands. This player is said to be the “commander” of the block.
192 Command blocks support placeholders, insert one of these placerholders and they will be replaced by a player name:
193 • “@c”: commander of this command block
194 • “@n” or “@p”: nearest player from the command block
195 • “@f” farthest player from the command block
196 • “@r”: random player currently in the world
197 • “@@”: literal “@” sign
199 Example 1:
200 time 12000
202 Sets the game clock to 12:00
204 Example 2:
205 give @n mcl_core:apple 5
207 → Gives the nearest player 5 apples]],
209 tiles = {{name="jeija_commandblock_off.png", animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=2}}},
210 groups = {creative_breakable=1, mesecon_effector_off=1},
211 drop = "",
212 on_blast = function() end,
213 on_construct = construct,
214 is_ground_content = false,
215 on_place = on_place,
216 after_place_node = after_place,
217 on_rightclick = on_rightclick,
218 sounds = mcl_sounds.node_sound_stone_defaults(),
219 mesecons = {effector = {
220 action_on = commandblock_action_on,
221 rules = mesecon.rules.alldirs,
223 _mcl_blast_resistance = 18000000,
224 _mcl_hardness = -1,
227 minetest.register_node("mesecons_commandblock:commandblock_on", {
228 tiles = {{name="jeija_commandblock_off.png", animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=2}}},
229 groups = {creative_breakable=1, mesecon_effector_on=1, not_in_creative_inventory=1},
230 drop = "",
231 on_blast = function() end,
232 on_construct = construct,
233 is_ground_content = false,
234 on_place = on_place,
235 after_place_node = after_place,
236 on_rightclick = on_rightclick,
237 sounds = mcl_sounds.node_sound_stone_defaults(),
238 mesecons = {effector = {
239 action_off = commandblock_action_off,
240 rules = mesecon.rules.alldirs,
242 _mcl_blast_resistance = 18000000,
243 _mcl_hardness = -1,
246 minetest.register_on_player_receive_fields(function(player, formname, fields)
247 if string.sub(formname, 1, 13) == "commandblock_" then
248 if not fields.submit and not fields.doc then
249 return
251 local privs = minetest.get_player_privs(player:get_player_name())
252 if not privs.maphack then
253 minetest.chat_send_player(player:get_player_name(), "Access denied. You need the “maphack” privilege to edit command blocks.")
254 return
257 if fields.doc and minetest.get_modpath("doc") then
258 doc.show_entry(player:get_player_name(), "nodes", "mesecons_commandblock:commandblock_off", true)
259 return
261 local index, _, x, y, z = string.find(formname, "commandblock_(-?%d+)_(-?%d+)_(-?%d+)")
262 if index ~= nil and x ~= nil and y ~= nil and z ~= nil then
263 local pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
264 local meta = minetest.get_meta(pos)
265 if not minetest.settings:get_bool("creative_mode") then
266 minetest.chat_send_player(player:get_player_name(), "Editing the command block has failed! You can only change the command block in Creative Mode!")
267 return
269 local check, error_message = check_commands(fields.commands, player:get_player_name())
270 if check == false then
271 -- Command block rejected
272 minetest.chat_send_player(player:get_player_name(), error_message)
273 return
274 else
275 meta:set_string("commands", fields.commands)
277 else
278 minetest.chat_send_player(player:get_player_name(), "Editing the command block has failed! The command block is gone.")
281 end)
283 -- Add entry alias for the Help
284 if minetest.get_modpath("doc") then
285 doc.add_entry_alias("nodes", "mesecons_commandblock:commandblock_off", "nodes", "mesecons_commandblock:commandblock_on")