Allows admins to run code snippets without crashing the server as often.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

console.lua 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. --
  2. -- Snippet console - Allows players to create and edit persistent snippets
  3. --
  4. local forms = {}
  5. minetest.register_on_leaveplayer(function(player)
  6. forms[player:get_player_name()] = nil
  7. end)
  8. local callback
  9. function snippets.update_console(name)
  10. if not minetest.check_player_privs(name, 'server') then return end
  11. if not forms[name] then
  12. forms[name] = snippets.Form(name)
  13. forms[name]:add_callback(callback)
  14. forms[name].context.code = ''
  15. end
  16. local form = forms[name]
  17. if not form:is_open() then form.context.text = nil end
  18. local formspec = 'size[14,10]' ..
  19. 'label[0,0;My snippets]' ..
  20. 'textlist[0,0.5;3.5,7.4;snippetlist;#aaaaaaNew snippet'
  21. local snippet_list = {}
  22. form.context.snippet_list = snippet_list
  23. for k, v in pairs(snippets.registered_snippets) do
  24. if v.persistent then
  25. table.insert(snippet_list, k)
  26. end
  27. end
  28. table.sort(snippet_list)
  29. local selected, unaved = 0, false
  30. local selected_snippet = form.context.selected_snippet
  31. for id, snippet in ipairs(snippet_list) do
  32. formspec = formspec .. ',##' .. minetest.formspec_escape(snippet)
  33. if snippet == selected_snippet then
  34. selected = id
  35. local def = snippets.registered_snippets[snippet]
  36. if (def and def.code or '') ~= form.context.code then
  37. formspec = formspec .. ' (unsaved)'
  38. end
  39. end
  40. end
  41. formspec = formspec .. ';' .. tostring(selected + 1) .. ']' ..
  42. 'button[0,8.1;3.7,0.75;save;Save]' ..
  43. 'button[0,8.85;3.7,0.75;save_as;Save as]' ..
  44. 'button_exit[0,9.6;3.7,0.75;quit;Quit]'
  45. formspec = formspec ..
  46. 'textlist[3.9,6.01;10,4.04;ignore;'
  47. if form.context.text then
  48. local console_text = form.context.text
  49. if #console_text > 0 then
  50. for id, msg in ipairs(console_text) do
  51. if id > 1 then formspec = formspec .. ',' end
  52. formspec = formspec .. minetest.formspec_escape(msg)
  53. end
  54. formspec = formspec .. ',;' .. (#console_text + 1)
  55. else
  56. formspec = formspec .. ';1'
  57. end
  58. formspec = formspec ..
  59. ']button[3.9,5.14;10.21,0.81;reset;Reset]' ..
  60. 'box[3.9,0.4;10,4.5;#ffffff]'
  61. else
  62. formspec = formspec .. ';1]' ..
  63. 'button[3.9,5.14;10.21,0.81;run;Run]'
  64. end
  65. if not form.context.code then form.context.code = '' end
  66. local code = minetest.formspec_escape(form.context.code)
  67. if code == '' and form.context.text then code = '(no code)' end
  68. local snippet, owner
  69. if selected_snippet then
  70. snippet = minetest.colorize('#aaa', selected_snippet)
  71. else
  72. snippet = minetest.colorize('#888', 'New snippet')
  73. end
  74. local def = snippets.registered_snippets[selected_snippet]
  75. if def and def.owner then
  76. owner = minetest.colorize('#aaa', def.owner)
  77. elseif selected_snippet then
  78. owner = minetest.colorize('#888', 'none')
  79. else
  80. owner = minetest.colorize('#aaa', name)
  81. end
  82. formspec = formspec .. ']textarea[4.2,0.4;10.2,5.31;' ..
  83. (form.context.text and '' or 'code') .. ';Snippet: ' ..
  84. minetest.formspec_escape(snippet .. ', owner: ' .. owner) .. ';' ..
  85. code .. ']'
  86. form:set_formspec(formspec)
  87. end
  88. function snippets.show_console(name)
  89. snippets.update_console(name)
  90. local form = forms[name]
  91. if form then form:show() end
  92. end
  93. function snippets.push_console_msg(name, msg, col)
  94. if not col or col:sub(1, 1) ~= '#' or #col ~= 7 then
  95. col = '##'
  96. end
  97. local text = forms[name] and forms[name].context.text
  98. if text then
  99. table.insert(text, col .. tostring(msg))
  100. snippets.update_console(name)
  101. end
  102. end
  103. snippets.register_on_log(function(snippet, level, msg)
  104. local owner = snippets.registered_snippets[snippet].owner
  105. local form = forms[owner]
  106. if not owner or not form or not form.context.text or
  107. not form:is_open() then
  108. return
  109. end
  110. if level ~= 'none' then
  111. msg = level:sub(1, 1):upper() .. level:sub(2) .. ': ' .. msg
  112. end
  113. local col
  114. if level == 'warning' then
  115. col = '#FFFF00'
  116. elseif level == 'error' then
  117. col = '#FF0000'
  118. elseif level == 'debug' then
  119. col = '#888888'
  120. end
  121. local p = snippet:sub(1, 16) == 'snippets:player_'
  122. if not p then msg = 'From snippet "' .. snippet .. '": ' .. msg end
  123. snippets.push_console_msg(owner, msg, col)
  124. if p then return true end
  125. end)
  126. minetest.register_chatcommand('snippets', {
  127. description = 'Opens the snippets console.',
  128. privs = {server=true},
  129. func = function(name, param)
  130. snippets.show_console(name)
  131. return true, 'Opened the snippets console.'
  132. end,
  133. })
  134. local function saveform_callback(saveform, fields)
  135. local name = saveform.pname
  136. local form = forms[name]
  137. saveform:close()
  138. -- Sanity check
  139. if not minetest.check_player_privs(name, 'server') or not form then
  140. forms[name] = nil
  141. return
  142. end
  143. if not fields.filename or fields.filename == '' then
  144. minetest.chat_send_player(name, 'Save operation cancelled.')
  145. snippets.show_console(name)
  146. return
  147. end
  148. -- Don't overwrite non-persistent snippets
  149. local filename = fields.filename:gsub(':', '/')
  150. while snippets.registered_snippets[filename] and
  151. not snippets.registered_snippets[filename].persistent do
  152. filename = filename .. '_'
  153. end
  154. -- Actually save it
  155. snippets.register_snippet(filename, {
  156. owner = name,
  157. code = form.context.code,
  158. persistent = true,
  159. })
  160. form.context.selected_snippet = filename
  161. snippets.show_console(name)
  162. end
  163. function callback(form, fields)
  164. local name = form.pname
  165. if not minetest.check_player_privs(name, 'server') then
  166. forms[name] = nil
  167. form:close()
  168. end
  169. if fields.code then
  170. form.context.code = fields.code
  171. end
  172. if fields.ignore then
  173. return
  174. elseif fields.run then
  175. local code = fields.code
  176. form.context.text = {}
  177. snippets.show_console(name)
  178. if not code or code == '' then return end
  179. local res = snippets.exec_as_player(name, code)
  180. if res ~= nil then
  181. snippets.push_console_msg(name, res)
  182. end
  183. elseif fields.reset then
  184. form.context.text = nil
  185. snippets.update_console(name)
  186. elseif fields.snippetlist and form.context.snippet_list then
  187. local event = minetest.explode_textlist_event(fields.snippetlist)
  188. local selected = form.context.snippet_list[event.index - 1]
  189. if form.context.selected_snippet == selected then return end
  190. form.context.selected_snippet = selected
  191. form.context.text = nil
  192. local def = snippets.registered_snippets[selected]
  193. form.context.code = def and def.code or ''
  194. snippets.update_console(name)
  195. elseif fields.save and form.context.selected_snippet then
  196. if form.context.code == '' then
  197. snippets.unregister_snippet(form.context.selected_snippet)
  198. form.context.selected_snippet = nil
  199. else
  200. snippets.register_snippet(form.context.selected_snippet, {
  201. owner = name,
  202. code = form.context.code,
  203. persistent = true,
  204. })
  205. end
  206. snippets.show_console(name)
  207. elseif fields.save or fields.save_as and form.context.code ~= '' then
  208. form.context.text = nil
  209. local saveform = snippets.Form(name)
  210. saveform:set_formspec(
  211. 'field[filename;Please enter a new snippet name.;]')
  212. saveform:add_callback(saveform_callback)
  213. saveform:show()
  214. elseif fields.quit then
  215. form.text = nil
  216. end
  217. end