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,
25 terminal - an interactive terminal
31 local function get_cmd_params(line
)
34 for w
in line
:gmatch("%w+") do
37 elseif params
== "" then
40 params
= params
.. " " .. w
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",
58 telex
= "run the telex command - send and receive messages"
62 help
= "display help information for subcommands",
63 list
= "list received telex messages",
64 draft
= "create a new, or edit an outgoing telex message",
65 discard
= "discard the current telex draft message",
66 send
= "send the current draft telex message to a recipient",
67 remove = "remove a received telex message by number",
68 read = "read a received telex message by number",
69 reply
= "create a draft reply to a message by number"
72 local function make_formspec(output
, prompt
)
75 "field_close_on_enter[input;false]" ..
76 "textlist[0.4,0.5;11,6;output;"
79 for part
in output
:gmatch("[^\r\n]+") do
80 f
= f
.. minetest
.formspec_escape(part
) .. ","
83 f
= f
.. minetest
.formspec_escape(prompt
) .. ";" .. c
.. ";false]"
85 f
= f
.. "field[0.7,7;11.2,1;input;;]"
90 clear
= function(output
, params
, c
)
93 append
= function(output
, params
, c
)
95 return output
.. "\nError: No write access"
97 local what
, _
= get_cmd_params(params
)
99 return output
.. "\nError: Missing file name"
102 return output
.. "\nWriting \"" .. what
.. "\". Enter STOP on a line by itself to finish"
104 write = function(output
, params
, c
)
106 return output
.. "\nError: No write access"
108 local what
, _
= get_cmd_params(params
)
110 return output
.. "\nError: Missing file name"
113 local meta
= minetest
.get_meta(c
.pos
)
114 local meta_files
= meta
:get_string("files")
115 if meta_files
and meta_files
~= "" then
116 local files
= minetest
.parse_json(meta_files
) or {}
117 if files
and files
[what
] then
119 meta
:set_string("files", minetest
.write_json(files
))
120 meta
:mark_as_private("files")
123 return output
.. "\nWriting \"" .. what
.. "\". Enter STOP on a line by itself to finish"
125 edit
= function(output
, params
, c
)
127 return output
.. "\nError: No write access"
129 local what
, _
= get_cmd_params(params
)
131 return output
.. "\nError: Missing file name"
137 local meta
= minetest
.get_meta(c
.pos
)
138 local meta_files
= meta
:get_string("files")
139 if meta_files
and meta_files
~= "" then
140 local files
= minetest
.parse_json(meta_files
) or {}
141 if files
and files
[what
] then
146 fsc
.show(c
.name
, "size[12,8]" ..
147 "textarea[0.5,0.5;11.5,7.0;text;text;" ..
148 minetest
.formspec_escape(text
) .. "]" ..
149 "button[5.2,7.2;1.6,0.5;exit;Save]",
155 remove = function(output
, params
, c
)
157 return output
.. "\nError: No write access"
159 local meta
= minetest
.get_meta(c
.pos
)
160 local meta_files
= meta
:get_string("files")
161 if not meta_files
or meta_files
== "" then
162 return output
.. "\nError: No such file"
164 local files
= minetest
.parse_json(meta_files
) or {}
165 local first
, _
= get_cmd_params(params
)
169 return output
.. "\nError: No such file"
171 meta
:set_string("files", minetest
.write_json(files
))
172 meta
:mark_as_private("files")
173 return output
.. "\nRemoved \"" .. first
.. "\""
175 list
= function(output
, params
, c
)
176 local meta
= minetest
.get_meta(c
.pos
)
177 local meta_files
= meta
:get_string("files")
179 if not meta_files
or meta_files
== "" then
180 return output
.. "\nError: No files found"
182 files
= minetest
.parse_json(meta_files
) or {}
184 return output
.. "\nError: No files found"
186 for k
, _
in pairs(files
) do
187 output
= output
.. "\n" .. k
191 echo
= function(output
, params
, c
)
192 return output
.. "\n" .. params
194 read = function(output
, params
, c
)
195 local meta
= minetest
.get_meta(c
.pos
)
196 local meta_files
= meta
:get_string("files")
197 if not meta_files
or meta_files
== "" then
198 return output
.. "\nError: No such file"
200 local files
= minetest
.parse_json(meta_files
) or {}
201 local first
, _
= get_cmd_params(params
)
203 if first
== "rules" then
204 rules
.show(c
.name
, "player")
207 return output
.. "\n" .. files
[first
]
209 return output
.. "\nError: No such file"
212 lock
= function(output
, params
, c
)
214 return output
.. "\nError: no write access"
216 local meta
= minetest
.get_meta(c
.pos
)
217 meta
:set_int("locked", 1)
218 meta
:mark_as_private("locked")
219 return output
.. "\n" .. "Terminal locked"
221 unlock
= function(output
, params
, c
)
222 return output
.. "\n" .. "Error: unable to connect to authentication service"
224 telex
= function(output
, params
, c
)
226 local h
, p
= get_cmd_params(params
)
231 for k
, _
in pairs(term
.telex_help
) do
235 for _
, v
in ipairs(ot
) do
236 o
= o
.. " " .. v
.. "\n"
238 return output
.. "\n" ..
239 "Available subcommands:\n" ..
241 "Type `telex help <subcommand>` for more help about that command"
242 elseif term
.telex_help
[p
] then
243 return output
.. "\n" .. term
.telex_help
[p
]
245 return output
.. "\nError: No help for \"" .. h
.. "\""
247 elseif h
== "list" then
248 local player
= minetest
.get_player_by_name(c
.name
)
249 return output
.. "\n" .. table.concat(telex
.list(player
), "\n")
250 elseif h
== "draft" then
251 local player
= minetest
.get_player_by_name(c
.name
)
252 local meta
= player
:get_meta()
253 local text
= meta
:get_string("telex_draft")
254 local subject
= meta
:get_string("telex_subject")
255 fsc
.show(c
.name
, "size[12,8]" ..
256 "field[0.5,0.5;11.5,1;subject;subject;" ..
257 minetest
.formspec_escape(subject
) .. "]" ..
258 "textarea[0.5,1.5;11.5,7.0;text;text;" ..
259 minetest
.formspec_escape(text
) .. "]" ..
260 "button[5.2,7.7;1.6,0.5;exit;Save]",
265 elseif h
== "discard" then
266 local player
= minetest
.get_player_by_name(c
.name
)
267 local meta
= player
:get_meta()
268 meta
:set_string("telex_draft", "")
269 meta
:set_string("telex_subject", "")
270 return output
.. "\n" .. "Draft erased."
271 elseif h
== "send" then
273 return output
.. "\n" .. "Need recipient name to send draft message to."
276 local player
= minetest
.get_player_by_name(c
.name
)
277 local meta
= player
:get_meta()
278 local text
= meta
:get_string("telex_draft")
279 local subject
= meta
:get_string("telex_subject")
282 return output
.. "\n" .. "No draft exists. Cannot send. run `telex draft` to create a draft."
285 if subject
== "" then
286 return output
.. "\n" .. "No draft subject. Edit your draft and enter a subject."
293 content
= string.split(text
, "\n")
297 return output
.. "\n" .. "Mail sent to <" .. p
.. ">."
298 elseif h
== "read" then
299 local player
= minetest
.get_player_by_name(c
.name
)
300 return output
.. "\n" .. table.concat(telex
.read(player
, tonumber(p
)), "\n")
301 elseif h
== "remove" then
302 local player
= minetest
.get_player_by_name(c
.name
)
303 return output
.. "\n" .. table.concat(telex
.delete(player
, tonumber(p
)), "\n")
304 elseif h
== "reply" then
305 local player
= minetest
.get_player_by_name(c
.name
)
306 local msg
= telex
.get(player
, tonumber(p
))
307 if not msg
.subject
then
308 return output
.. "\n" .. table.concat(msg
, "\n")
310 fsc
.show(c
.name
, "size[12,8]" ..
311 "field[0.5,0.5;11.5,1;subject;subject;" ..
312 minetest
.formspec_escape("Re: " .. msg
.subject
) .. "]" ..
313 "textarea[0.5,1.5;11.5,7.0;text;text;" ..
315 minetest
.formspec_escape(table.concat(msg
.content
, "\n> ")) .. "]" ..
316 "button[5.2,7.7;1.6,0.5;exit;Save]",
322 return output
.. "\n" .. "Invalid command passed. Use `telex help` to list available commands."
325 return output
.. "\n" ..
326 "Type `telex help` for more help about the telex command"
329 help
= function(output
, params
, c
)
331 local h
, _
= get_cmd_params(params
)
333 return output
.. "\n" .. term
.help
[h
]
335 return output
.. "\nError: No help for \"" .. h
.. "\""
340 for k
, _
in pairs(term
.help
) do
344 for _
, v
in ipairs(ot
) do
345 o
= o
.. " " .. v
.. "\n"
347 return output
.. "\n" ..
348 "Available commands:\n" ..
350 "Type `help <command>` for more help about that command"
354 function term
.recv(player
, fields
, context
)
356 local name
= player
:get_player_name()
358 if not c
or not c
.pos
then
359 log.fs_data(player
, name
, "term.recv/context", fields
)
363 local line
= fields
.input
364 if line
and line
~= "" then
365 local output
= c
.output
or ""
366 minetest
.sound_play("terminal_keyboard_clicks", {pos
= c
.pos
})
369 -- this shouldn't get reached, but just to be safe, check ro
372 output
= output
.. "\n" .. line
373 output
= output
.. "\nError: no write access"
376 make_formspec(output
, "> "),
381 -- are we writing a file?
382 if line
== "STOP" then
383 -- done writing a file
385 output
= output
.. "\n" .. line
388 make_formspec(output
, "> "),
393 local meta
= minetest
.get_meta(c
.pos
)
394 local meta_files
= meta
:get_string("files")
396 if not meta_files
or meta_files
== "" then
397 files
[c
.writing
] = line
399 files
= minetest
.parse_json(meta_files
) or {}
400 if not files
[c
.writing
] then
401 files
[c
.writing
] = line
403 files
[c
.writing
] = files
[c
.writing
] .. "\n" .. line
406 if string.len(files
[c
.writing
]) < 16384 then
407 local json
= minetest
.write_json(files
)
408 if string.len(json
) < 49152 then
409 meta
:set_string("files", json
)
410 meta
:mark_as_private("files")
411 output
= output
.. "\n" .. line
413 output
= output
.. "\n" .. "Error: no space left on device"
416 output
= output
.. "\n" .. "Error: maximum file length exceeded"
420 make_formspec(output
, ""),
426 output
= output
.. "\n> " .. line
428 local meta
= minetest
.get_meta(c
.pos
)
429 local cmd
, params
= get_cmd_params(line
)
430 if meta
:get_int("locked") == 1 and cmd
~= "unlock" then
431 output
= output
.. "\nError: Terminal locked, type \"unlock\" to unlock it"
434 make_formspec(output
, "> "),
440 local fn
= term
.commands
[cmd
]
442 output
= fn(output
, params
, c
)
444 output
= output
.. "\n" .. "Error: Syntax Error. Try \"help\""
446 if output
~= false then
449 make_formspec(output
, "> "),
455 elseif fields
.quit
then
456 minetest
.sound_play("terminal_power_off", {pos
= c
.pos
})
458 elseif fields
.output
then
459 -- CHG events - do not return true
461 elseif fields
.input
then
462 -- KEYBOARD events - do not return true
466 log.fs_data(player
, name
, "term.recv/default", fields
)
470 function term
.edit(player
, fields
, context
)
471 if not fields
.text
then
475 local name
= player
:get_player_name()
477 if not c
or not c
.pos
or not c
.output
then
478 log.fs_data(player
, name
, "term.edit/terminal", fields
)
481 local output
= c
.output
484 output
= output
.. "\n" .. "Error: no such file\n"
486 make_formspec(output
, "> "),
492 local meta
= minetest
.get_meta(c
.pos
)
493 local meta_files
= meta
:get_string("files")
495 files
= minetest
.parse_json(meta_files
) or {}
496 files
[c
.what
] = fields
.text
499 local json
= minetest
.write_json(files
)
500 if string.len(json
) < 49152 then
501 meta
:set_string("files", json
)
502 meta
:mark_as_private("files")
503 output
= output
.. "\n" .. "Wrote: " .. c
.what
.. "\n"
505 output
= output
.. "\n" .. "Error: no space left on device\n"
511 make_formspec(output
, "> "),
517 function term
.draft(player
, fields
, context
)
518 if not fields
.text
then
522 local name
= player
:get_player_name()
524 if not c
or not c
.pos
or not c
.output
then
525 log.fs_data(player
, name
, "term.draft/terminal", fields
)
528 local output
= c
.output
530 local meta
= player
:get_meta()
533 if string.len(fields
.text
) < 49152 then
534 meta
:set_string("telex_draft", fields
.text
)
535 meta
:set_string("telex_subject", fields
.subject
)
536 output
= output
.. "\n" .. "Draft saved.\n"
538 output
= output
.. "\n" .. "Error: no space left on device\n"
544 make_formspec(output
, "> "),
550 local terminal_use
= function(pos
, node
, clicker
, itemstack
, pointed_thing
)
554 local name
= clicker
:get_player_name()
561 if boxes
.players_editing_boxes
[name
] or
562 (not boxes
.players_in_boxes
[name
] and minetest
.check_player_privs(clicker
, "server")) then
566 -- send formspec to player
568 make_formspec("", "> "),
571 minetest
.sound_play("terminal_power_on", {pos
= pos
})
572 -- trigger on first use
573 local meta
= minetest
.get_meta(pos
)
574 if meta
:get_int("locked") ~= 1 then
576 minetest
.after(1.0, mech
.untrigger
, pos
)
580 minetest
.register_node("terminal:terminal", {
581 description
= "Interactive terminal console emulator access interface unit controller",
583 mesh
= "terminal.obj",
584 groups
= {mech
= 1, trigger
= 1},
586 {name
= "terminal_base.png"},
587 {name
= "terminal_idle.png", animation
= {type = "vertical_frames", aspect_w
= 14, aspect_h
= 13, length
= 4.0}},
590 paramtype2
= "facedir",
591 on_trigger
= function(pos
)
592 local meta
= minetest
.get_meta(pos
)
593 minetest
.sound_play("terminal_power_on", {pos
= pos
})
594 meta
:set_int("locked", 0)
595 meta
:mark_as_private("locked")
597 on_untrigger
= function(pos
)
598 local meta
= minetest
.get_meta(pos
)
599 minetest
.sound_play("terminal_power_off", {pos
= pos
})
600 meta
:set_int("locked", 1)
601 meta
:mark_as_private("locked")
603 on_rightclick
= terminal_use
,
604 sounds
= sounds
.metal
,