Split builtin.lua to multiple files
[oweals/minetest.git] / builtin / chatcommands.lua
1 -- Minetest: builtin/chatcommands.lua
2
3 --
4 -- Chat commands
5 --
6
7 minetest.chatcommands = {}
8 function minetest.register_chatcommand(cmd, def)
9         def = def or {}
10         def.params = def.params or ""
11         def.description = def.description or ""
12         def.privs = def.privs or {}
13         minetest.chatcommands[cmd] = def
14 end
15
16 -- Register the help command
17 minetest.register_chatcommand("help", {
18         privs = {},
19         params = "(nothing)/all/privs/<cmd>",
20         description = "Get help for commands or list privileges",
21         func = function(name, param)
22                 local format_help_line = function(cmd, def)
23                         local msg = "/"..cmd
24                         if def.params and def.params ~= "" then msg = msg .. " " .. def.params end
25                         if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end
26                         return msg
27                 end
28                 if param == "" then
29                         local msg = ""
30                         cmds = {}
31                         for cmd, def in pairs(minetest.chatcommands) do
32                                 if minetest.check_player_privs(name, def.privs) then
33                                         table.insert(cmds, cmd)
34                                 end
35                         end
36                         minetest.chat_send_player(name, "Available commands: "..table.concat(cmds, " "))
37                         minetest.chat_send_player(name, "Use '/help <cmd>' to get more information, or '/help all' to list everything.")
38                 elseif param == "all" then
39                         minetest.chat_send_player(name, "Available commands:")
40                         for cmd, def in pairs(minetest.chatcommands) do
41                                 if minetest.check_player_privs(name, def.privs) then
42                                         minetest.chat_send_player(name, format_help_line(cmd, def))
43                                 end
44                         end
45                 elseif param == "privs" then
46                         minetest.chat_send_player(name, "Available privileges:")
47                         for priv, def in pairs(minetest.registered_privileges) do
48                                 minetest.chat_send_player(name, priv..": "..def.description)
49                         end
50                 else
51                         local cmd = param
52                         def = minetest.chatcommands[cmd]
53                         if not def then
54                                 minetest.chat_send_player(name, "Command not available: "..cmd)
55                         else
56                                 minetest.chat_send_player(name, format_help_line(cmd, def))
57                         end
58                 end
59         end,
60 })
61
62 -- Register C++ commands without functions
63 minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)"})
64 minetest.register_chatcommand("status", {description = "print server status line"})
65 minetest.register_chatcommand("shutdown", {params = "", description = "shutdown server", privs = {server=true}})
66 minetest.register_chatcommand("setting", {params = "<name> = <value>", description = "set line in configuration file", privs = {server=true}})
67 minetest.register_chatcommand("clearobjects", {params = "", description = "clear all objects in world", privs = {server=true}})
68 minetest.register_chatcommand("time", {params = "<0...24000>", description = "set time of day", privs = {settime=true}})
69 minetest.register_chatcommand("ban", {params = "<name>", description = "ban IP of player", privs = {ban=true}})
70 minetest.register_chatcommand("unban", {params = "<name/ip>", description = "remove IP ban", privs = {ban=true}})
71
72 -- Register some other commands
73 minetest.register_chatcommand("privs", {
74         params = "<name>",
75         description = "print out privileges of player",
76         func = function(name, param)
77                 if param == "" then
78                         param = name
79                 else
80                         if not minetest.check_player_privs(name, {privs=true}) then
81                                 minetest.chat_send_player(name, "Privileges of "..param.." are hidden from you.")
82                         end
83                 end
84                 minetest.chat_send_player(name, "Privileges of "..param..": "..minetest.privs_to_string(minetest.get_player_privs(param), ' '))
85         end,
86 })
87 minetest.register_chatcommand("grant", {
88         params = "<name> <privilege>|all",
89         description = "Give privilege to player",
90         privs = {privs=true},
91         func = function(name, param)
92                 local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
93                 if not grantname or not grantprivstr then
94                         minetest.chat_send_player(name, "Invalid parameters (see /help grant)")
95                         return
96                 end
97                 local grantprivs = minetest.string_to_privs(grantprivstr)
98                 if grantprivstr == "all" then
99                         grantprivs = minetest.registered_privileges
100                 end
101                 local privs = minetest.get_player_privs(grantname)
102                 for priv, _ in pairs(grantprivs) do
103                         privs[priv] = true
104                 end
105                 minetest.set_player_privs(grantname, privs)
106                 minetest.chat_send_player(name, "Privileges of "..grantname..": "..minetest.privs_to_string(minetest.get_player_privs(grantname), ' '))
107                 if grantname ~= name then
108                         minetest.chat_send_player(grantname, name.." granted you privileges: "..minetest.privs_to_string(grantprivs, ' '))
109                 end
110         end,
111 })
112 minetest.register_chatcommand("revoke", {
113         params = "<name> <privilege>|all",
114         description = "Remove privilege from player",
115         privs = {privs=true},
116         func = function(name, param)
117                 local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)")
118                 if not revokename or not revokeprivstr then
119                         minetest.chat_send_player(name, "Invalid parameters (see /help revoke)")
120                         return
121                 end
122                 local revokeprivs = minetest.string_to_privs(revokeprivstr)
123                 local privs = minetest.get_player_privs(revokename)
124                 if revokeprivstr == "all" then
125                         privs = {}
126                 else
127                         for priv, _ in pairs(revokeprivs) do
128                                 privs[priv] = nil
129                         end
130                 end
131                 minetest.set_player_privs(revokename, privs)
132                 minetest.chat_send_player(name, "Privileges of "..revokename..": "..minetest.privs_to_string(minetest.get_player_privs(revokename), ' '))
133                 if revokename ~= name then
134                         minetest.chat_send_player(revokename, name.." revoked privileges from you: "..minetest.privs_to_string(revokeprivs, ' '))
135                 end
136         end,
137 })
138 minetest.register_chatcommand("setpassword", {
139         params = "<name> <password>",
140         description = "set given password",
141         privs = {password=true},
142         func = function(name, param)
143                 if param == "" then
144                         minetest.chat_send_player(name, "Password field required")
145                         return
146                 end
147                 minetest.set_player_password(name, param)
148                 minetest.chat_send_player(name, "Password set")
149         end,
150 })
151 minetest.register_chatcommand("clearpassword", {
152         params = "<name>",
153         description = "set empty password",
154         privs = {password=true},
155         func = function(name, param)
156                 minetest.set_player_password(name, '')
157                 minetest.chat_send_player(name, "Password cleared")
158         end,
159 })
160
161 minetest.register_chatcommand("auth_reload", {
162         params = "",
163         description = "reload authentication data",
164         privs = {server=true},
165         func = function(name, param)
166                 local done = minetest.auth_reload()
167                 if done then
168                         minetest.chat_send_player(name, "Done.")
169                 else
170                         minetest.chat_send_player(name, "Failed.")
171                 end
172         end,
173 })
174
175 minetest.register_chatcommand("teleport", {
176         params = "<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>",
177         description = "teleport to given position",
178         privs = {teleport=true},
179         func = function(name, param)
180                 -- Returns (pos, true) if found, otherwise (pos, false)
181                 local function find_free_position_near(pos)
182                         local tries = {
183                                 {x=1,y=0,z=0},
184                                 {x=-1,y=0,z=0},
185                                 {x=0,y=0,z=1},
186                                 {x=0,y=0,z=-1},
187                         }
188                         for _, d in ipairs(tries) do
189                                 local p = {x = pos.x+d.x, y = pos.y+d.y, z = pos.z+d.z}
190                                 local n = minetest.env:get_node(p)
191                                 if not minetest.registered_nodes[n.name].walkable then
192                                         return p, true
193                                 end
194                         end
195                         return pos, false
196                 end
197
198                 local teleportee = nil
199                 local p = {}
200                 p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
201                 teleportee = minetest.env:get_player_by_name(name)
202                 if teleportee and p.x and p.y and p.z then
203                         minetest.chat_send_player(name, "Teleporting to ("..p.x..", "..p.y..", "..p.z..")")
204                         teleportee:setpos(p)
205                         return
206                 end
207                 
208                 local teleportee = nil
209                 local p = nil
210                 local target_name = nil
211                 target_name = string.match(param, "^([^ ]+)$")
212                 teleportee = minetest.env:get_player_by_name(name)
213                 if target_name then
214                         local target = minetest.env:get_player_by_name(target_name)
215                         if target then
216                                 p = target:getpos()
217                         end
218                 end
219                 if teleportee and p then
220                         p = find_free_position_near(p)
221                         minetest.chat_send_player(name, "Teleporting to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")")
222                         teleportee:setpos(p)
223                         return
224                 end
225                 
226                 if minetest.check_player_privs(name, {bring=true}) then
227                         local teleportee = nil
228                         local p = {}
229                         local teleportee_name = nil
230                         teleportee_name, p.x, p.y, p.z = string.match(param, "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
231                         if teleportee_name then
232                                 teleportee = minetest.env:get_player_by_name(teleportee_name)
233                         end
234                         if teleportee and p.x and p.y and p.z then
235                                 minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to ("..p.x..", "..p.y..", "..p.z..")")
236                                 teleportee:setpos(p)
237                                 return
238                         end
239                         
240                         local teleportee = nil
241                         local p = nil
242                         local teleportee_name = nil
243                         local target_name = nil
244                         teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$")
245                         if teleportee_name then
246                                 teleportee = minetest.env:get_player_by_name(teleportee_name)
247                         end
248                         if target_name then
249                                 local target = minetest.env:get_player_by_name(target_name)
250                                 if target then
251                                         p = target:getpos()
252                                 end
253                         end
254                         if teleportee and p then
255                                 p = find_free_position_near(p)
256                                 minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")")
257                                 teleportee:setpos(p)
258                                 return
259                         end
260                 end
261
262                 minetest.chat_send_player(name, "Invalid parameters (\""..param.."\") or player not found (see /help teleport)")
263                 return
264         end,
265 })
266
267 --
268 -- Builtin chat handler
269 --
270
271 minetest.register_on_chat_message(function(name, message)
272         local cmd, param = string.match(message, "/([^ ]+) *(.*)")
273         if not param then
274                 param = ""
275         end
276         local cmd_def = minetest.chatcommands[cmd]
277         if cmd_def then
278                 if not cmd_def.func then
279                         -- This is a C++ command
280                         return false
281                 else
282                         local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs)
283                         if has_privs then
284                                 cmd_def.func(name, param)
285                         else
286                                 minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")")
287                         end
288                         return true -- handled chat message
289                 end
290         end
291         return false
292 end)
293
294