chatcommands.lua 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. ------------------------------------------------------------------------------
  2. -- This file is registered as reloadable.
  3. ------------------------------------------------------------------------------
  4. reload = reload or {}
  5. reload.impl = reload.impl or {}
  6. -- This function expects only to be called from the chatcommands that this mod registers!
  7. reload.impl.dofile = function(name, path)
  8. local PREFIX = reload.chat_prefix
  9. local FILEPATH = path
  10. if type(path) == "string" and type(name) == "string" then
  11. if path == "" then
  12. reload.chat_send_player(name, PREFIX .. "No filepath provided.")
  13. return false
  14. end
  15. -- Log this action.
  16. reload.log("action", "[Mod Reload] Player <" .. name .. "> attempts to execute Lua source file '" .. FILEPATH .. "'.")
  17. -- A bit of security.
  18. if string.find(path, "%.%.") then
  19. reload.chat_send_player(name, PREFIX .. "Filepath cannot include '..' tokens.")
  20. return false
  21. end
  22. -- Attempt to load and execute the Lua file.
  23. local func, err = loadfile(FILEPATH)
  24. if not func then -- Syntax error.
  25. reload.chat_send_player(name, PREFIX .. "Could not load file. Received error message: '" .. err .. "'.")
  26. return false
  27. end
  28. local good, err = pcall(func)
  29. if not good then -- Runtime error.
  30. reload.chat_send_player(name, PREFIX .. "Could not execute file. Received error message: '" .. err .. "'.")
  31. return false
  32. end
  33. reload.chat_send_player(name, PREFIX .. "File '" .. FILEPATH .. "' successfully executed.")
  34. return true
  35. else
  36. reload.chat_send_player(name, PREFIX .. "Invalid arguments.")
  37. return false
  38. end
  39. end
  40. -- This function expects only to be called from the chatcommands that this mod registers!
  41. reload.impl.reload = function(name, param)
  42. local PREFIX = reload.chat_prefix
  43. local ROOT = reload.root_path .. "/"
  44. local sparams = string.split(param, " ")
  45. local sfind = nil
  46. -- total files count
  47. local itotal = 0
  48. -- search result count
  49. local ifound = 0
  50. if sparams[1] == "list" then
  51. if type(sparams[2]) == "string" then
  52. sfind = sparams[2]
  53. end
  54. for k, v in pairs(reload.impl.files) do
  55. -- counting reloadable files
  56. itotal = itotal + 1
  57. local found = true
  58. if sfind then
  59. found = string.find(k, sfind, 1, false)
  60. end
  61. if found then
  62. -- counting search results
  63. ifound = ifound + 1
  64. -- get root relative path
  65. local srelpath = string.gsub(v, "^" .. ROOT, "")
  66. local sfmt = "%s %-32s '%s'."
  67. local sfileid = "<" .. k .. ">"
  68. local smsg = string.format(sfmt, PREFIX, sfileid, srelpath)
  69. reload.chat_send_player(name, smsg)
  70. end
  71. end
  72. local smsg = string.format("%s Listed %d of %d reloadable files.",
  73. PREFIX, ifound, itotal)
  74. reload.chat_send_player(name, smsg)
  75. reload.chat_send_player(name, PREFIX .. "End of list.")
  76. return true
  77. else
  78. if not param or param == "" then
  79. reload.chat_send_player(name, PREFIX .. "No file ID provided.")
  80. return false
  81. end
  82. local file = reload.impl.files[param]
  83. if file then
  84. return reload.impl.dofile(name, file)
  85. end
  86. reload.chat_send_player(name, PREFIX .. "Invalid file ID.")
  87. return false
  88. end
  89. end
  90. reload.impl.execute = function(name, path)
  91. local path2 = reload.root_path .. "/" .. path
  92. return reload.impl.dofile(name, path2)
  93. end
  94. reload.impl.dostring = function(name, str)
  95. local PREFIX = reload.chat_prefix
  96. if not str or str == "" then
  97. reload.chat_send_player(name, PREFIX .. "No argument provided.")
  98. reload.chat_send_player(name, PREFIX .. "Note: available custom variables are: me, mypos, player(name), print(text).")
  99. return false
  100. end
  101. -- Code injection.
  102. local ci = "do " .. -- Begin new block.
  103. "local me=minetest.get_player_by_name(\"" .. name .. "\") " ..
  104. "local mypos=me:get_pos() " ..
  105. "local myname=me:get_player_name() " ..
  106. "local function player(pname) return minetest.get_player_by_name(pname) end " ..
  107. "local function print(text) minetest.chat_send_player(\"" .. name .. "\", \"# Server: \" .. text) end " ..
  108. str .. " end" -- User code & end of block.
  109. local func, err = loadstring(ci)
  110. if not func then -- Syntax error.
  111. reload.chat_send_player(name, PREFIX .. "Could not compile string. Received error message: '" .. err .. "'.")
  112. return false
  113. end
  114. local good, err = pcall(func)
  115. if not good then -- Runtime error.
  116. reload.chat_send_player(name, PREFIX .. "Could not execute string. Received error message: '" .. err .. "'.")
  117. return false
  118. end
  119. reload.chat_send_player(name, PREFIX .. "Code executed successfully!")
  120. return true
  121. end
  122. -- Don't register the chat commands more than once, even if this file is reloaded.
  123. if not reload.chat_registered then
  124. minetest.register_chatcommand("reload", {
  125. params = "<fileid> | list [search]",
  126. description = "Reload a registered source file at runtime.",
  127. -- Player must have server priviliges.
  128. privs = {server=true},
  129. func = function(...) reload.impl.reload(...) end,
  130. })
  131. minetest.register_chatcommand("exec", {
  132. params = "<filepath>",
  133. description = "Load and execute an arbitrary Lua source file.",
  134. -- Player must have server priviliges.
  135. privs = {server=true},
  136. func = function(...) reload.impl.execute(...) end,
  137. })
  138. minetest.register_chatcommand("run", {
  139. params = "<filepath>",
  140. description = "Load and execute an arbitrary Lua source file.",
  141. -- Player must have server priviliges.
  142. privs = {server=true},
  143. func = function(...) reload.impl.execute(...) end,
  144. })
  145. minetest.register_chatcommand("dofile", {
  146. params = "<filepath>",
  147. description = "Load and execute an arbitrary Lua source file.",
  148. -- Player must have server priviliges.
  149. privs = {server=true},
  150. func = function(...) reload.impl.execute(...) end,
  151. })
  152. -- Alias name. Some people (like me) keep wanting to spell it out.
  153. minetest.register_chatcommand("execute", {
  154. params = "<filepath>",
  155. description = "Load and execute an arbitrary Lua source file.",
  156. -- Player must have server priviliges.
  157. privs = {server=true},
  158. func = function(...) reload.impl.execute(...) end,
  159. })
  160. minetest.register_chatcommand("dostring", {
  161. params = "<code>",
  162. description = "Execute a statement in Lua.",
  163. -- Player must have server priviliges.
  164. privs = {server=true},
  165. func = function(...) reload.impl.dostring(...) end,
  166. })
  167. minetest.register_chatcommand("lua", {
  168. params = "<code>",
  169. description = "Execute a statement in Lua.",
  170. -- Player must have server priviliges.
  171. privs = {server=true},
  172. func = function(...) reload.impl.dostring(...) end,
  173. })
  174. minetest.register_chatcommand("dolua", {
  175. params = "<code>",
  176. description = "Execute a statement in Lua.",
  177. -- Player must have server priviliges.
  178. privs = {server=true},
  179. func = function(...) reload.impl.dostring(...) end,
  180. })
  181. reload.chat_registered = true
  182. end