Update installation instructions - adopted from Wuzzy's notes.
[insidethebox/insidethebox_wuzzy.git] / mods / terminal / init.lua
blobfba0b44a4a954ab7fb70a3fb2bd08597437ba4c4
2 --[[
4 ITB (insidethebox) minetest game - Copyright (C) 2017-2018 sofar & nore
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public License
8 as published by the Free Software Foundation; either version 2.1
9 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19 MA 02111-1307 USA
21 ]]--
23 --[[
25 terminal - an interactive terminal
27 ]]--
29 local term = {}
31 local function get_cmd_params(line)
32 local cmd = ""
33 local params = ""
34 for w in line:gmatch("%w+") do
35 if cmd == "" then
36 cmd = w
37 elseif params == "" then
38 params = w
39 else
40 params = params .. " " .. w
41 end
42 end
43 return cmd, params
44 end
46 term.help = {
47 append = "append text to a file",
48 clear = "clear the output",
49 echo = "echoes the input back to you",
50 help = "display help information for commands",
51 list = "list available files",
52 lock = "lock the terminal",
53 read = "read the content of a file",
54 remove = "removes a file",
55 unlock = "unlocks the terminal",
56 write = "write text to a file",
57 edit = "edits a file in an editor",
60 local function make_formspec(output, prompt)
61 local f =
62 "size[12,8]" ..
63 "field_close_on_enter[input;false]" ..
64 "textlist[0.4,0.5;11,6;output;"
66 local c = 1
67 for part in output:gmatch("[^\r\n]+") do
68 f = f .. minetest.formspec_escape(part) .. ","
69 c = c + 1
70 end
71 f = f .. minetest.formspec_escape(prompt) .. ";" .. c .. ";false]"
73 f = f .. "field[0.7,7;11.2,1;input;;]"
74 return f
75 end
77 term.commands = {
78 clear = function(output, params, c)
79 return ""
80 end,
81 append = function(output, params, c)
82 if not c.rw then
83 return output .. "\nError: No write access"
84 end
85 local what, _ = get_cmd_params(params)
86 if what == "" then
87 return output .. "\nError: Missing file name"
88 end
89 c.writing = what
90 return output .. "\nWriting \"" .. what .. "\". Enter STOP on a line by itself to finish"
91 end,
92 write = function(output, params, c)
93 if not c.rw then
94 return output .. "\nError: No write access"
95 end
96 local what, _ = get_cmd_params(params)
97 if what == "" then
98 return output .. "\nError: Missing file name"
99 end
100 c.writing = what
101 local meta = minetest.get_meta(c.pos)
102 local meta_files = meta:get_string("files")
103 if meta_files and meta_files ~= "" then
104 local files = minetest.parse_json(meta_files) or {}
105 if files and files[what] then
106 files[what] = ""
107 meta:set_string("files", minetest.write_json(files))
108 meta:mark_as_private("files")
111 return output .. "\nWriting \"" .. what .. "\". Enter STOP on a line by itself to finish"
112 end,
113 edit = function(output, params, c)
114 if not c.rw then
115 return output .. "\nError: No write access"
117 local what, _ = get_cmd_params(params)
118 if what == "" then
119 return output .. "\nError: Missing file name"
121 c.what = what
122 c.output = output
124 local text = ""
125 local meta = minetest.get_meta(c.pos)
126 local meta_files = meta:get_string("files")
127 if meta_files and meta_files ~= "" then
128 local files = minetest.parse_json(meta_files) or {}
129 if files and files[what] then
130 text = files[what]
134 fsc.show(c.name, "size[12,8]" ..
135 "textarea[0.5,0.5;11.5,7.0;text;text;" ..
136 minetest.formspec_escape(text) .. "]" ..
137 "button_exit[5.2,7.2;1.6,0.5;exit;Save]",
139 term.edit)
141 return false
142 end,
143 remove = function(output, params, c)
144 if not c.rw then
145 return output .. "\nError: No write access"
147 local meta = minetest.get_meta(c.pos)
148 local meta_files = meta:get_string("files")
149 if not meta_files or meta_files == "" then
150 return output .. "\nError: No such file"
152 local files = minetest.parse_json(meta_files) or {}
153 local first, _ = get_cmd_params(params)
154 if files[first] then
155 files[first] = nil
156 else
157 return output .. "\nError: No such file"
159 meta:set_string("files", minetest.write_json(files))
160 meta:mark_as_private("files")
161 return output .. "\nRemoved \"" .. first .. "\""
162 end,
163 list = function(output, params, c)
164 local meta = minetest.get_meta(c.pos)
165 local meta_files = meta:get_string("files")
166 local files
167 if not meta_files or meta_files == "" then
168 return output .. "\nError: No files found"
170 files = minetest.parse_json(meta_files) or {}
171 if not files then
172 return output .. "\nError: No files found"
174 for k, _ in pairs(files) do
175 output = output .. "\n" .. k
177 return output
178 end,
179 echo = function(output, params, c)
180 return output .. "\n" .. params
181 end,
182 read = function(output, params, c)
183 local meta = minetest.get_meta(c.pos)
184 local meta_files = meta:get_string("files")
185 if not meta_files or meta_files == "" then
186 return output .. "\nError: No such file"
188 local files = minetest.parse_json(meta_files) or {}
189 local first, _ = get_cmd_params(params)
190 if files[first] then
191 if first == "rules" then
192 rules.show(c.name, "player")
193 return false
195 return output .. "\n" .. files[first]
196 else
197 return output .. "\nError: No such file"
199 end,
200 lock = function(output, params, c)
201 if not c.rw then
202 return output .. "\nError: no write access"
204 local meta = minetest.get_meta(c.pos)
205 meta:set_int("locked", 1)
206 meta:mark_as_private("locked")
207 return output .. "\n" .. "Terminal locked"
208 end,
209 unlock = function(output, params, c)
210 return output .. "\n" .. "Error: unable to connect to authentication service"
211 end,
212 help = function(output, params, c)
213 if params ~= "" then
214 local h, _ = get_cmd_params(params)
215 if term.help[h] then
216 return output .. "\n" .. term.help[h]
217 else
218 return output .. "\nError: No help for \"" .. h .. "\""
221 local o = ""
222 local ot = {}
223 for k, _ in pairs(term.help) do
224 ot[#ot + 1] = k
226 table.sort(ot)
227 for _, v in ipairs(ot) do
228 o = o .. " " .. v .. "\n"
230 return output .. "\n" ..
231 "Available commands:\n" ..
232 o ..
233 "Type `help <command>` for more help about that command"
234 end,
237 function term.recv(player, fields, context)
238 -- input validation
239 local name = player:get_player_name()
240 local c = context
241 if not c or not c.pos then
242 log.fs_data(player, name, "terminal:", fields)
243 return true
246 local line = fields.input
247 if line and line ~= "" then
248 local output = c.output or ""
249 minetest.sound_play("terminal_keyboard_clicks", {pos = c.pos})
251 if c.writing then
252 -- this shouldn't get reached, but just to be safe, check ro
253 if not c.rw then
254 c.writing = nil
255 output = output .. "\n" .. line
256 output = output .. "\nError: no write access"
257 c.output = output
258 fsc.show(name,
259 make_formspec(output, "> "),
261 term.recv)
262 return
264 -- are we writing a file?
265 if line == "STOP" then
266 -- done writing a file
267 c.writing = nil
268 output = output .. "\n" .. line
269 c.output = output
270 fsc.show(name,
271 make_formspec(output, "> "),
273 term.recv)
274 return
276 local meta = minetest.get_meta(c.pos)
277 local meta_files = meta:get_string("files")
278 local files = {}
279 if not meta_files or meta_files == "" then
280 files[c.writing] = line
281 else
282 files = minetest.parse_json(meta_files) or {}
283 if not files[c.writing] then
284 files[c.writing] = line
285 else
286 files[c.writing] = files[c.writing] .. "\n" .. line
289 if string.len(files[c.writing]) < 16384 then
290 local json = minetest.write_json(files)
291 if string.len(json) < 49152 then
292 meta:set_string("files", json)
293 meta:mark_as_private("files")
294 output = output .. "\n" .. line
295 else
296 output = output .. "\n" .. "Error: no space left on device"
298 else
299 output = output .. "\n" .. "Error: maximum file length exceeded"
301 c.output = output
302 fsc.show(name,
303 make_formspec(output, ""),
305 term.recv)
306 return
307 else
308 -- else parse cmd
309 output = output .. "\n> " .. line
311 local meta = minetest.get_meta(c.pos)
312 local cmd, params = get_cmd_params(line)
313 if meta:get_int("locked") == 1 and cmd ~= "unlock" then
314 output = output .. "\nError: Terminal locked, type \"unlock\" to unlock it"
315 c.output = output
316 fsc.show(name,
317 make_formspec(output, "> "),
319 term.recv)
320 return
323 local fn = term.commands[cmd]
324 if fn then
325 output = fn(output, params, c)
326 else
327 output = output .. "\n" .. "Error: Syntax Error. Try \"help\""
329 if output ~= false then
330 c.output = output
331 fsc.show(name,
332 make_formspec(output, "> "),
334 term.recv)
336 return
338 elseif fields.quit then
339 minetest.sound_play("terminal_power_off", {pos = c.pos})
340 return true
341 elseif fields.output then
342 -- CHG events - do not return true
343 return
346 log.fs_data(player, name, "terminal:", fields)
347 return true
350 function term.edit(player, fields, context)
351 if not fields.text then
352 return true
355 local name = player:get_player_name()
356 local c = context
357 if not c or not c.pos or not c.output then
358 log.fs_data(player, name, "terminal:", fields)
359 return true
361 local output = c.output
363 if not c.what then
364 output = output .. "\n" .. "Error: no such file\n"
365 fsc.show(name,
366 make_formspec(output, "> "),
368 term.recv)
369 return
372 local meta = minetest.get_meta(c.pos)
373 local meta_files = meta:get_string("files")
374 local files
375 files = minetest.parse_json(meta_files) or {}
376 files[c.what] = fields.text
378 -- validate it fits
379 local json = minetest.write_json(files)
380 if string.len(json) < 49152 then
381 meta:set_string("files", json)
382 meta:mark_as_private("files")
383 output = output .. "\n" .. "Wrote: " .. c.what .. "\n"
384 else
385 output = output .. "\n" .. "Error: no space left on device\n"
388 fsc.show(name,
389 make_formspec(output, "> "),
391 term.recv)
392 return
395 local terminal_use = function(pos, node, clicker, itemstack, pointed_thing)
396 if not clicker then
397 return
399 local name = clicker:get_player_name()
400 local context = {
401 pos = pos,
402 rw = false,
403 output = "",
404 name = name,
406 if boxes.players_editing_boxes[name] or
407 (not boxes.players_in_boxes[name] and minetest.check_player_privs(clicker, "server")) then
408 -- allow rw access
409 context.rw = true
411 -- send formspec to player
412 fsc.show(name,
413 make_formspec("", "> "),
414 context,
415 term.recv)
416 minetest.sound_play("terminal_power_on", {pos = pos})
417 -- trigger on first use
418 local meta = minetest.get_meta(pos)
419 if meta:get_int("locked") ~= 1 then
420 mech.trigger(pos)
421 minetest.after(1.0, mech.untrigger, pos)
425 minetest.register_node("terminal:terminal", {
426 description = "An interactive terminal console emulator access interface unit controller",
427 drawtype = "mesh",
428 mesh = "terminal.obj",
429 groups = {mech = 1, trigger = 1},
430 tiles = {
431 {name = "terminal_base.png"},
432 {name = "terminal_idle.png", animation = {type = "vertical_frames", aspect_w = 14, aspect_h = 13, length = 4.0}},
434 paramtype = "light",
435 paramtype2 = "facedir",
436 on_trigger = function(pos)
437 local meta = minetest.get_meta(pos)
438 minetest.sound_play("terminal_power_on", {pos = pos})
439 meta:set_int("locked", 0)
440 meta:mark_as_private("locked")
441 end,
442 on_untrigger = function(pos)
443 local meta = minetest.get_meta(pos)
444 minetest.sound_play("terminal_power_off", {pos = pos})
445 meta:set_int("locked", 1)
446 meta:mark_as_private("locked")
447 end,
448 on_rightclick = terminal_use,