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.

forms.lua 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. --
  2. -- Minetest snippets mod: A formspec API
  3. --
  4. -- This should probably be put in formspeclib.
  5. --
  6. local open_formspecs = {}
  7. minetest.register_on_leaveplayer(function(player)
  8. local name = player:get_player_name()
  9. open_formspecs[name] = nil
  10. end)
  11. local get_player_by_name = minetest.global_exists('cloaking') and
  12. cloaking.get_player_by_name or minetest.get_player_by_name
  13. -- Formspec objects
  14. -- You can create one of these per player and handle input
  15. local Form = {}
  16. local forms = {}
  17. setmetatable(forms, {__mode = 'k'})
  18. local function get(form)
  19. if not forms[form] then
  20. error('snippets.Form method called on a non-Form!', 3)
  21. end
  22. return forms[form]
  23. end
  24. -- Get unique formnames
  25. local used_ids = {}
  26. setmetatable(used_ids, {__mode = 'v'})
  27. local function get_next_formname(form)
  28. -- Iterate over it because of inconsistencies when getting the length of a
  29. -- list containing nil.
  30. local id = 1
  31. for _ in ipairs(used_ids) do id = id + 1 end
  32. -- ID should be equal to #used_ids + 1.
  33. used_ids[id] = form
  34. return 'snippets:form_' .. id
  35. end
  36. -- Override minetest.show_formspec
  37. local show_formspec = minetest.show_formspec
  38. function minetest.show_formspec(pname, formname, formspec)
  39. if pname and (formspec ~= '' or formname == '') then
  40. open_formspecs[pname] = nil
  41. end
  42. return show_formspec(pname, formname, formspec)
  43. end
  44. -- Show formspecs
  45. function Form:show()
  46. local data = get(self)
  47. if not get_player_by_name(data.victim) then return false end
  48. open_formspecs[data.victim] = self
  49. local formspec = data.prepend .. data.formspec .. data.append
  50. if formspec == '' then formspec = ' ' end
  51. show_formspec(data.victim, data.formname, formspec)
  52. return true
  53. end
  54. Form.open = Form.show
  55. -- Close formspecs
  56. function Form:close()
  57. local data = get(self)
  58. if open_formspecs[data.victim] == self then
  59. minetest.close_formspec(data.victim, data.formname)
  60. open_formspecs[data.victim] = nil
  61. end
  62. end
  63. Form.hide = Form.close
  64. -- Check if the form is open
  65. function Form:is_open()
  66. return open_formspecs[get(self).victim] == self
  67. end
  68. -- Prepends etc
  69. function Form:get_prepend() return get(self).prepend end
  70. function Form:get_formspec() return get(self).formspec end
  71. function Form:get_append() return get(self).append end
  72. function Form:set_prepend(text)
  73. local data = get(self)
  74. data.prepend = tostring(text or '')
  75. if open_formspecs[data.victim] == self then self:show() end
  76. end
  77. function Form:set_formspec(text)
  78. local data = get(self)
  79. data.formspec = tostring(text or '')
  80. if open_formspecs[data.victim] == self then self:show() end
  81. end
  82. function Form:set_append(text)
  83. local data = get(self)
  84. data.append = tostring(text or '')
  85. if open_formspecs[data.victim] == self then self:show() end
  86. end
  87. -- Callbacks
  88. function Form:add_callback(...)
  89. local data, argc = get(self), select('#', ...)
  90. local event, func
  91. if argc == 1 then
  92. event, func = '', ...
  93. elseif argc == 2 then
  94. event, func = ...
  95. if type(event) ~= 'string' then
  96. error('Invalid usage for snippets.Form:add_callback().', 2)
  97. end
  98. else
  99. error('snippets.Form:add_callback() takes one or two arguments.', 2)
  100. end
  101. if not data.callbacks[event] then data.callbacks[event] = {} end
  102. table.insert(data.callbacks[event], snippets.wrap_callback(func))
  103. end
  104. -- Create a Form object
  105. function snippets.Form(name)
  106. if minetest.is_player(name) then
  107. name = name:get_player_name()
  108. elseif type(name) ~= 'string' or not get_player_by_name(name) then
  109. error('Attempted to create a Form for a non-existent player!', 2)
  110. end
  111. local form = {context = {}, pname = name}
  112. setmetatable(form, {__index = Form})
  113. forms[form] = {
  114. victim = name, prepend = '', formspec = '', append = '',
  115. callbacks = {}, formname = get_next_formname(form),
  116. }
  117. return form
  118. end
  119. function snippets.close_form(name)
  120. if minetest.is_player(name) then name = name:get_player_name() end
  121. if open_formspecs[name] then open_formspecs[name]:close() end
  122. end
  123. -- Callbacks
  124. local function run_callbacks(callbacks, ...)
  125. if not callbacks then return end
  126. for _, func in ipairs(callbacks) do func(...) end
  127. end
  128. minetest.register_on_player_receive_fields(function(player, formname, fields)
  129. if formname:sub(1, 14) ~= 'snippets:form_' then return end
  130. local pname = player:get_player_name()
  131. local form = open_formspecs[pname]
  132. local data = forms[form]
  133. if not data or data.formname ~= formname then return end
  134. -- Nuke the formspec if required
  135. if fields.quit then open_formspecs[pname] = nil end
  136. -- Run generic callbacks
  137. run_callbacks(data.callbacks[''], form, fields)
  138. -- Run field-specific callbacks
  139. for k, v in pairs(fields) do
  140. run_callbacks(data.callbacks[k], form, fields)
  141. end
  142. end)