Tweak command block formspec
[MineClone/MineClone2.git] / mods / ITEMS / REDSTONE / mesecons_commandblock / init.lua
blob3bc1b9ac1f550f83a183efd01dc68b6c4792de4e
2 local function initialize_data(meta)
3 local commands = minetest.formspec_escape(meta:get_string("commands"))
4 end
6 local function construct(pos)
7 local meta = minetest.get_meta(pos)
9 meta:set_string("commands", "")
10 initialize_data(meta)
11 end
13 local function after_place(pos, placer)
14 if placer then
15 local meta = minetest.get_meta(pos)
16 initialize_data(meta)
17 end
18 end
20 local function resolve_commands(commands, pos)
21 local players = minetest.get_connected_players()
23 -- No players online: remove all commands containing
24 -- @nearest, @farthest and @random
25 if #players == 0 then
26 commands = commands:gsub("[^\r\n]+", function (line)
27 if line:find("@nearest") then return "" end
28 if line:find("@farthest") then return "" end
29 if line:find("@random") then return "" 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 return commands
53 end
55 local function check_commands(commands, player_name)
56 for _, command in pairs(commands:split("\n")) do
57 local pos = command:find(" ")
58 local cmd, param = command, ""
59 if pos then
60 cmd = command:sub(1, pos - 1)
61 end
62 local cmddef = minetest.chatcommands[cmd]
63 if not cmddef then
64 -- Invalid chat command
65 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."
66 if string.sub(cmd, 1, 1) == "/" then
67 msg = msg .. " Hint: Try to remove the trailing slash."
68 end
69 return false, msg
70 end
71 if player_name then
72 local player_privs = minetest.get_player_privs(player_name)
74 for cmd_priv, _ in pairs(cmddef.privs) do
75 if player_privs[cmd_priv] ~= true then
76 local msg = "Error: You have insufficient privileges to use the command “"..cmd.."” (missing privilege: "..cmd_priv..")! The command block has not been changed."
77 return false, msg
78 end
79 end
80 end
81 end
82 return true
83 end
85 local function commandblock_action_on(pos, node)
86 if node.name ~= "mesecons_commandblock:commandblock_off" then
87 return
88 end
90 minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_on"})
92 local meta = minetest.get_meta(pos)
94 local commands = resolve_commands(meta:get_string("commands"), pos)
95 for _, command in pairs(commands:split("\n")) do
96 local pos = command:find(" ")
97 local cmd, param = command, ""
98 if pos then
99 cmd = command:sub(1, pos - 1)
100 param = command:sub(pos + 1)
102 local cmddef = minetest.chatcommands[cmd]
103 if not cmddef then
104 -- Invalid chat command
105 return
107 local dummy_player = ""
108 cmddef.func(dummy_player, param)
112 local function commandblock_action_off(pos, node)
113 if node.name == "mesecons_commandblock:commandblock_on" then
114 minetest.swap_node(pos, {name = "mesecons_commandblock:commandblock_off"})
118 local on_rightclick = function(pos, node, player, itemstack, pointed_thing)
119 -- Only allow access in Creative Mode
120 if not minetest.settings:get_bool("creative_mode") then
121 return
123 local privs = minetest.get_player_privs(player:get_player_name())
124 if not privs.maphack then
125 minetest.chat_send_player(player:get_player_name(), "Access denied. You need the “maphack” privilege to edit command blocks.")
126 return
129 local meta = minetest.get_meta(pos)
130 local commands = meta:get_string("commands")
131 local formspec = "invsize[9,5;]" ..
132 "textarea[0.5,0.5;8.5,4;commands;Commands;"..commands.."]" ..
133 "button_exit[3.3,4.5;2,1;submit;Submit]" ..
134 "image_button[8,4.5;1,1;doc_button_icon_lores.png;doc;]" ..
135 "tooltip[doc;Help]"
136 minetest.show_formspec(player:get_player_name(), "commandblock_"..pos.x.."_"..pos.y.."_"..pos.z, formspec)
139 local on_place = function(itemstack, placer, pointed_thing)
140 if pointed_thing.type ~= "node" then
141 return itemstack
144 -- Use pointed node's on_rightclick function first, if present
145 local node = minetest.get_node(pointed_thing.under)
146 if placer and not placer:get_player_control().sneak then
147 if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
148 return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
152 local privs = minetest.get_player_privs(placer:get_player_name())
153 if not privs.maphack then
154 minetest.chat_send_player(placer:get_player_name(), "Placement denied. You need the “maphack” privilege to place command blocks.")
155 return itemstack
158 return minetest.item_place_node(itemstack, placer, pointed_thing)
161 minetest.register_node("mesecons_commandblock:commandblock_off", {
162 description = "Command Block",
164 _doc_items_longdesc =
165 "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.",
166 _doc_items_usagehelp =
167 [[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.
169 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.
171 You can optionally use the following placeholders in your commands:
172 • “@nearest” is replaced by the name of the player nearest to the command block
173 • “@farthest” is replaced by the name of the player farthest away from the command block
174 • “@random” is replaced by the name of a random player currently connected
176 Example 1:
177 time 12000
179 Sets the game clock to 12:00
181 Example 2:
182 give @nearest mcl_core:apple 5
184 → Gives the nearest player 5 apples]],
186 tiles = {{name="jeija_commandblock_off.png", animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=2}}},
187 groups = {creative_breakable=1, mesecon_effector_off=1},
188 drop = "",
189 on_blast = function() end,
190 on_construct = construct,
191 is_ground_content = false,
192 on_place = on_place,
193 after_place_node = after_place,
194 on_rightclick = on_rightclick,
195 sounds = mcl_sounds.node_sound_stone_defaults(),
196 mesecons = {effector = {
197 action_on = commandblock_action_on,
198 rules = mesecon.rules.alldirs,
200 _mcl_blast_resistance = 18000000,
201 _mcl_hardness = -1,
204 minetest.register_node("mesecons_commandblock:commandblock_on", {
205 tiles = {{name="jeija_commandblock_off.png", animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=2}}},
206 groups = {creative_breakable=1, mesecon_effector_on=1, not_in_creative_inventory=1},
207 drop = "",
208 on_blast = function() end,
209 on_construct = construct,
210 is_ground_content = false,
211 on_place = on_place,
212 after_place_node = after_place,
213 on_rightclick = on_rightclick,
214 sounds = mcl_sounds.node_sound_stone_defaults(),
215 mesecons = {effector = {
216 action_off = commandblock_action_off,
217 rules = mesecon.rules.alldirs,
219 _mcl_blast_resistance = 18000000,
220 _mcl_hardness = -1,
223 minetest.register_on_player_receive_fields(function(player, formname, fields)
224 if string.sub(formname, 1, 13) == "commandblock_" then
225 if not fields.submit and not fields.doc then
226 return
228 local privs = minetest.get_player_privs(player:get_player_name())
229 if not privs.maphack then
230 minetest.chat_send_player(player:get_player_name(), "Access denied. You need the “maphack” privilege to edit command blocks.")
231 return
234 if fields.doc and minetest.get_modpath("doc") then
235 doc.show_entry(player:get_player_name(), "nodes", "mesecons_commandblock:commandblock_off", true)
236 return
238 local index, _, x, y, z = string.find(formname, "commandblock_(-?%d+)_(-?%d+)_(-?%d+)")
239 if index ~= nil and x ~= nil and y ~= nil and z ~= nil then
240 local pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
241 local meta = minetest.get_meta(pos)
242 if not minetest.settings:get_bool("creative_mode") then
243 minetest.chat_send_player(player:get_player_name(), "Editing the command block has failed! You can only change the command block in Creative Mode!")
244 return
246 local check, error_message = check_commands(fields.commands, player:get_player_name())
247 if check == false then
248 -- Command block rejected
249 minetest.chat_send_player(player:get_player_name(), error_message)
250 return
251 else
252 meta:set_string("commands", fields.commands)
253 initialize_data(meta)
255 else
256 minetest.chat_send_player(player:get_player_name(), "Editing the command block has failed! The command block is gone.")
259 end)
261 -- Add entry alias for the Help
262 if minetest.get_modpath("doc") then
263 doc.add_entry_alias("nodes", "mesecons_commandblock:commandblock_off", "nodes", "mesecons_commandblock:commandblock_on")