Add @commander placeholder to command blocks
[MineClone/MineClone2.git] / mods / ITEMS / REDSTONE / mesecons_commandblock / init.lua
blobb41f20d90856852e818ffa146facf9dcca1d56ae
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 -- No players online: remove all commands containing
23 -- “@” placeholders
24 if #players == 0 then
25 commands = commands:gsub("[^\r\n]+", function (line)
26 if line:find("@nearest") then return "" end
27 if line:find("@farthest") then return "" end
28 if line:find("@random") then return "" end
29 if line:find("@commander") then return commander end
30 return line
31 end)
32 return commands
33 end
35 local nearest, farthest = nil, nil
36 local min_distance, max_distance = math.huge, -1
37 for index, player in pairs(players) do
38 local distance = vector.distance(pos, player:getpos())
39 if distance < min_distance then
40 min_distance = distance
41 nearest = player:get_player_name()
42 end
43 if distance > max_distance then
44 max_distance = distance
45 farthest = player:get_player_name()
46 end
47 end
48 local random = players[math.random(#players)]:get_player_name()
49 commands = commands:gsub("@nearest", nearest)
50 commands = commands:gsub("@farthest", farthest)
51 commands = commands:gsub("@random", random)
52 commands = commands:gsub("@commander", commander)
53 return commands
54 end
56 local function check_commands(commands, player_name)
57 for _, command in pairs(commands:split("\n")) do
58 local pos = command:find(" ")
59 local cmd, param = command, ""
60 if pos then
61 cmd = command:sub(1, pos - 1)
62 end
63 local cmddef = minetest.chatcommands[cmd]
64 if not cmddef then
65 -- Invalid chat command
66 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."
67 if string.sub(cmd, 1, 1) == "/" then
68 msg = msg .. " Hint: Try to remove the trailing slash."
69 end
70 return false, msg
71 end
72 if player_name then
73 local player_privs = minetest.get_player_privs(player_name)
75 for cmd_priv, _ in pairs(cmddef.privs) do
76 if player_privs[cmd_priv] ~= true then
77 local msg = "Error: You have insufficient privileges to use the command “"..cmd.."” (missing privilege: "..cmd_priv..")! The command block has not been changed."
78 return false, msg
79 end
80 end
81 end
82 end
83 return true
84 end
86 local function commandblock_action_on(pos, node)
87 if node.name ~= "mesecons_commandblock:commandblock_off" then
88 return
89 end
91 minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_on"})
93 local meta = minetest.get_meta(pos)
95 local commands = resolve_commands(meta:get_string("commands"), pos)
96 for _, command in pairs(commands:split("\n")) do
97 local cpos = command:find(" ")
98 local cmd, param = command, ""
99 if cpos then
100 cmd = command:sub(1, cpos - 1)
101 param = command:sub(cpos + 1)
103 local cmddef = minetest.chatcommands[cmd]
104 if not cmddef then
105 -- Invalid chat command
106 return
108 -- Execute command in the name of commander
109 local commander = meta:get_string("commander")
110 cmddef.func(commander, param)
114 local function commandblock_action_off(pos, node)
115 if node.name == "mesecons_commandblock:commandblock_on" then
116 minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_off"})
120 local on_rightclick = function(pos, node, player, itemstack, pointed_thing)
121 -- Only allow access in Creative Mode
122 if not minetest.settings:get_bool("creative_mode") then
123 return
125 local privs = minetest.get_player_privs(player:get_player_name())
126 if not privs.maphack then
127 minetest.chat_send_player(player:get_player_name(), "Access denied. You need the “maphack” privilege to edit command blocks.")
128 return
131 local meta = minetest.get_meta(pos)
132 local commands = meta:get_string("commands")
133 local commander = meta:get_string("commander")
134 local commanderstr
135 if commander == "" or commander == nil then
136 commanderstr = "Error: No commander! Block must be replaced."
137 else
138 commanderstr = "Commander: "..commander
140 local formspec = "invsize[9,5;]" ..
141 "textarea[0.5,0.5;8.5,4;commands;Commands;"..commands.."]" ..
142 "button_exit[3.3,4.5;2,1;submit;Submit]" ..
143 "image_button[8,4.5;1,1;doc_button_icon_lores.png;doc;]" ..
144 "label[0,4;"..minetest.formspec_escape(commanderstr).."]" ..
145 "tooltip[doc;Help]"
146 minetest.show_formspec(player:get_player_name(), "commandblock_"..pos.x.."_"..pos.y.."_"..pos.z, formspec)
149 local on_place = function(itemstack, placer, pointed_thing)
150 if pointed_thing.type ~= "node" then
151 return itemstack
154 -- Use pointed node's on_rightclick function first, if present
155 local node = minetest.get_node(pointed_thing.under)
156 if placer and not placer:get_player_control().sneak then
157 if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
158 return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
162 local privs = minetest.get_player_privs(placer:get_player_name())
163 if not privs.maphack then
164 minetest.chat_send_player(placer:get_player_name(), "Placement denied. You need the “maphack” privilege to place command blocks.")
165 return itemstack
168 return minetest.item_place_node(itemstack, placer, pointed_thing)
171 minetest.register_node("mesecons_commandblock:commandblock_off", {
172 description = "Command Block",
174 _doc_items_longdesc =
175 "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.",
176 _doc_items_usagehelp =
177 [[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.
179 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.
181 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.
183 Command blocks support placeholders, insert one of these placerholders and they will be replaced by a player name:
184 • “@commander”: commander of this command block
185 • “@nearest”: nearest player from the command block
186 • “@farthest” farthest player from the command block
187 • “@random”: random player currently in the world
189 Example 1:
190 time 12000
192 Sets the game clock to 12:00
194 Example 2:
195 give @nearest mcl_core:apple 5
197 → Gives the nearest player 5 apples]],
199 tiles = {{name="jeija_commandblock_off.png", animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=2}}},
200 groups = {creative_breakable=1, mesecon_effector_off=1},
201 drop = "",
202 on_blast = function() end,
203 on_construct = construct,
204 is_ground_content = false,
205 on_place = on_place,
206 after_place_node = after_place,
207 on_rightclick = on_rightclick,
208 sounds = mcl_sounds.node_sound_stone_defaults(),
209 mesecons = {effector = {
210 action_on = commandblock_action_on,
211 rules = mesecon.rules.alldirs,
213 _mcl_blast_resistance = 18000000,
214 _mcl_hardness = -1,
217 minetest.register_node("mesecons_commandblock:commandblock_on", {
218 tiles = {{name="jeija_commandblock_off.png", animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=2}}},
219 groups = {creative_breakable=1, mesecon_effector_on=1, not_in_creative_inventory=1},
220 drop = "",
221 on_blast = function() end,
222 on_construct = construct,
223 is_ground_content = false,
224 on_place = on_place,
225 after_place_node = after_place,
226 on_rightclick = on_rightclick,
227 sounds = mcl_sounds.node_sound_stone_defaults(),
228 mesecons = {effector = {
229 action_off = commandblock_action_off,
230 rules = mesecon.rules.alldirs,
232 _mcl_blast_resistance = 18000000,
233 _mcl_hardness = -1,
236 minetest.register_on_player_receive_fields(function(player, formname, fields)
237 if string.sub(formname, 1, 13) == "commandblock_" then
238 if not fields.submit and not fields.doc then
239 return
241 local privs = minetest.get_player_privs(player:get_player_name())
242 if not privs.maphack then
243 minetest.chat_send_player(player:get_player_name(), "Access denied. You need the “maphack” privilege to edit command blocks.")
244 return
247 if fields.doc and minetest.get_modpath("doc") then
248 doc.show_entry(player:get_player_name(), "nodes", "mesecons_commandblock:commandblock_off", true)
249 return
251 local index, _, x, y, z = string.find(formname, "commandblock_(-?%d+)_(-?%d+)_(-?%d+)")
252 if index ~= nil and x ~= nil and y ~= nil and z ~= nil then
253 local pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
254 local meta = minetest.get_meta(pos)
255 if not minetest.settings:get_bool("creative_mode") then
256 minetest.chat_send_player(player:get_player_name(), "Editing the command block has failed! You can only change the command block in Creative Mode!")
257 return
259 local check, error_message = check_commands(fields.commands, player:get_player_name())
260 if check == false then
261 -- Command block rejected
262 minetest.chat_send_player(player:get_player_name(), error_message)
263 return
264 else
265 meta:set_string("commands", fields.commands)
267 else
268 minetest.chat_send_player(player:get_player_name(), "Editing the command block has failed! The command block is gone.")
271 end)
273 -- Add entry alias for the Help
274 if minetest.get_modpath("doc") then
275 doc.add_entry_alias("nodes", "mesecons_commandblock:commandblock_off", "nodes", "mesecons_commandblock:commandblock_on")