An advanced-ish waypoints mod for Minetest.
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.

sscsm.lua 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. --
  2. -- Minetest advmarkers SSCSM
  3. --
  4. advmarkers = {}
  5. local data = {}
  6. -- Convert positions to/from strings
  7. local function pos_to_string(pos)
  8. if type(pos) == 'table' then
  9. pos = minetest.pos_to_string(vector.round(pos))
  10. end
  11. if type(pos) == 'string' then
  12. return pos
  13. end
  14. end
  15. local function string_to_pos(pos)
  16. if type(pos) == 'string' then
  17. pos = minetest.string_to_pos(pos)
  18. end
  19. if type(pos) == 'table' then
  20. return vector.round(pos)
  21. end
  22. end
  23. -- Run remote command
  24. -- This is easier to do with chatcommands because servers cannot send mod
  25. -- channel messages to specific clients.
  26. local csm_key = string.char(1) .. 'ADVMARKERS_SSCSM' .. string.char(1)
  27. local function run_remote_command(cmd, param)
  28. local msg = csm_key
  29. if cmd then
  30. msg = msg .. cmd
  31. if param then msg = msg .. param end
  32. end
  33. minetest.run_server_chatcommand('wp', msg)
  34. end
  35. -- Display a waypoint
  36. function advmarkers.display_waypoint(name)
  37. name = tostring(name)
  38. if data['marker-' .. name] then
  39. run_remote_command('0', tostring(name))
  40. return true
  41. else
  42. return false
  43. end
  44. end
  45. -- Get a waypoint
  46. function advmarkers.get_waypoint(name)
  47. return string_to_pos(data['marker-' .. tostring(name)])
  48. end
  49. advmarkers.get_marker = advmarkers.get_waypoint
  50. -- Delete a waypoint
  51. function advmarkers.delete_waypoint(name)
  52. name = tostring(name)
  53. if data['marker-' .. name] ~= nil then
  54. data['marker-' .. name] = nil
  55. run_remote_command('D', name)
  56. end
  57. end
  58. advmarkers.delete_marker = advmarkers.delete_waypoint
  59. -- Set a waypoint
  60. function advmarkers.set_waypoint(pos, name)
  61. pos = pos_to_string(pos)
  62. if not pos then return end
  63. name = tostring(name)
  64. data['marker-' .. name] = pos
  65. run_remote_command('S', pos:gsub(' ', '') .. ' ' .. name)
  66. return true
  67. end
  68. advmarkers.set_marker = advmarkers.set_waypoint
  69. -- Rename a waypoint and re-interpret the position.
  70. function advmarkers.rename_waypoint(oldname, newname)
  71. oldname, newname = tostring(oldname), tostring(newname)
  72. local pos = advmarkers.get_waypoint(oldname)
  73. if not pos or not advmarkers.set_waypoint(pos, newname) then return end
  74. if oldname ~= newname then
  75. advmarkers.delete_waypoint(oldname)
  76. end
  77. return true
  78. end
  79. advmarkers.rename_marker = advmarkers.rename_waypoint
  80. -- Import waypoints - Note that this won't import strings made by older
  81. -- versions of the CSM.
  82. function advmarkers.import(s, clear)
  83. if type(s) ~= 'table' then
  84. if s:sub(1, 1) ~= 'J' then return end
  85. s = minetest.decode_base64(s:sub(2))
  86. local success, msg = pcall(minetest.decompress, s)
  87. if not success then return end
  88. s = minetest.parse_json(msg)
  89. end
  90. -- Iterate over waypoints to preserve existing ones and check for errors.
  91. if type(s) ~= 'table' then return end
  92. if clear then data = {} end
  93. for name, pos in pairs(s) do
  94. if type(name) == 'string' and type(pos) == 'string' and
  95. name:sub(1, 7) == 'marker-' and minetest.string_to_pos(pos) and
  96. data[name] ~= pos then
  97. -- Prevent collisions
  98. local c = 0
  99. while data[name] and c < 50 do
  100. name = name .. '_'
  101. c = c + 1
  102. end
  103. -- Sanity check
  104. if c < 50 then
  105. data[name] = pos
  106. end
  107. end
  108. end
  109. end
  110. -- Get waypoint names
  111. function advmarkers.get_waypoint_names(sorted)
  112. local res = {}
  113. for name, pos in pairs(data) do
  114. if name:sub(1, 7) == 'marker-' then
  115. table.insert(res, name:sub(8))
  116. end
  117. end
  118. if sorted or sorted == nil then table.sort(res) end
  119. return res
  120. end
  121. -- Display the formspec
  122. local formspec_list = {}
  123. local selected_name = false
  124. function advmarkers.display_formspec()
  125. local formspec = 'size[5.25,8]' ..
  126. 'label[0,0;Waypoint list ' ..
  127. minetest.colorize('#888888', '(SSCSM)') .. ']' ..
  128. 'button_exit[0,7.5;1.3125,0.5;display;Display]' ..
  129. 'button[1.3125,7.5;1.3125,0.5;teleport;Teleport]' ..
  130. 'button[2.625,7.5;1.3125,0.5;rename;Rename]' ..
  131. 'button[3.9375,7.5;1.3125,0.5;delete;Delete]' ..
  132. 'textlist[0,0.75;5,6;marker;'
  133. -- Iterate over all the markers
  134. local id = 0
  135. local selected = 1
  136. formspec_list = advmarkers.get_waypoint_names()
  137. for id, name in ipairs(formspec_list) do
  138. if id > 1 then formspec = formspec .. ',' end
  139. if not selected_name then selected_name = name end
  140. if name == selected_name then selected = id end
  141. formspec = formspec .. '##' .. minetest.formspec_escape(name)
  142. end
  143. -- Close the text list and display the selected marker position
  144. formspec = formspec .. ';' .. tostring(selected) .. ']'
  145. if selected_name then
  146. local pos = advmarkers.get_marker(selected_name)
  147. if pos then
  148. pos = minetest.formspec_escape(tostring(pos.x) .. ', ' ..
  149. tostring(pos.y) .. ', ' .. tostring(pos.z))
  150. pos = 'Waypoint position: ' .. pos
  151. formspec = formspec .. 'label[0,6.75;' .. pos .. ']'
  152. end
  153. else
  154. -- Draw over the buttons
  155. formspec = formspec .. 'button_exit[0,7.5;5.25,0.5;quit;Close dialog]' ..
  156. 'label[0,6.75;No waypoints. Add one with "/add_wp".]'
  157. end
  158. -- Display the formspec
  159. return minetest.show_formspec('advmarkers-sscsm', formspec)
  160. end
  161. -- Register chatcommands
  162. local mrkr_cmd
  163. function mrkr_cmd(param)
  164. if param == '' then return advmarkers.display_formspec() end
  165. if param == '--ssm' then param = '' end
  166. minetest.run_server_chatcommand('wp', param)
  167. end
  168. sscsm.register_chatcommand('mrkr', mrkr_cmd)
  169. sscsm.register_chatcommand('wp', mrkr_cmd)
  170. sscsm.register_chatcommand('wps', mrkr_cmd)
  171. sscsm.register_chatcommand('waypoint', mrkr_cmd)
  172. sscsm.register_chatcommand('waypoints', mrkr_cmd)
  173. function mrkr_cmd(param)
  174. minetest.run_server_chatcommand('add_wp', param)
  175. run_remote_command()
  176. end
  177. sscsm.register_chatcommand('add_wp', mrkr_cmd)
  178. sscsm.register_chatcommand('add_waypoint', mrkr_cmd)
  179. sscsm.register_chatcommand('add_mrkr', mrkr_cmd)
  180. mrkr_cmd = nil
  181. -- Set the HUD
  182. minetest.register_on_formspec_input(function(formname, fields)
  183. if formname == 'advmarkers-ignore' then
  184. return true
  185. elseif formname ~= 'advmarkers-sscsm' then
  186. return
  187. end
  188. local name = false
  189. if fields.marker then
  190. local event = minetest.explode_textlist_event(fields.marker)
  191. if event.index then
  192. name = formspec_list[event.index]
  193. end
  194. else
  195. name = selected_name
  196. end
  197. if name then
  198. if fields.display then
  199. if not advmarkers.display_waypoint(name) then
  200. minetest.display_chat_message('Error displaying waypoint!')
  201. end
  202. elseif fields.rename then
  203. minetest.show_formspec('advmarkers-sscsm', 'size[6,3]' ..
  204. 'label[0.35,0.2;Rename waypoint]' ..
  205. 'field[0.3,1.3;6,1;new_name;New name;' ..
  206. minetest.formspec_escape(name) .. ']' ..
  207. 'button[0,2;3,1;cancel;Cancel]' ..
  208. 'button[3,2;3,1;rename_confirm;Rename]')
  209. elseif fields.rename_confirm then
  210. if fields.new_name and #fields.new_name > 0 then
  211. if advmarkers.rename_waypoint(name, fields.new_name) then
  212. selected_name = fields.new_name
  213. else
  214. minetest.display_chat_message('Error renaming waypoint!')
  215. end
  216. advmarkers.display_formspec()
  217. else
  218. minetest.display_chat_message(
  219. 'Please enter a new name for the waypoint.'
  220. )
  221. end
  222. elseif fields.teleport then
  223. minetest.show_formspec('advmarkers-sscsm', 'size[6,2.2]' ..
  224. 'label[0.35,0.25;' .. minetest.formspec_escape(
  225. 'Teleport to a waypoint\n - ' .. name
  226. ) .. ']' ..
  227. 'button[0,1.25;3,1;cancel;Cancel]' ..
  228. 'button_exit[3,1.25;3,1;teleport_confirm;Teleport]')
  229. elseif fields.teleport_confirm then
  230. -- Teleport with /teleport
  231. local pos = advmarkers.get_waypoint(name)
  232. if pos then
  233. minetest.run_server_chatcommand('teleport',
  234. pos.x .. ', ' .. pos.y .. ', ' .. pos.z)
  235. else
  236. minetest.display_chat_message('Error teleporting to waypoint!')
  237. end
  238. elseif fields.delete then
  239. minetest.show_formspec('advmarkers-sscsm', 'size[6,2]' ..
  240. 'label[0.35,0.25;Are you sure you want to delete this marker?]' ..
  241. 'button[0,1;3,1;cancel;Cancel]' ..
  242. 'button[3,1;3,1;delete_confirm;Delete]')
  243. elseif fields.delete_confirm then
  244. advmarkers.delete_waypoint(name)
  245. selected_name = false
  246. advmarkers.display_formspec()
  247. elseif fields.cancel then
  248. advmarkers.display_formspec()
  249. elseif name ~= selected_name then
  250. selected_name = name
  251. advmarkers.display_formspec()
  252. end
  253. elseif fields.display or fields.delete then
  254. minetest.display_chat_message('Please select a marker.')
  255. end
  256. return true
  257. end)
  258. -- Update the waypoint list
  259. minetest.register_on_receiving_chat_message(function(message)
  260. if message:sub(1, #csm_key) == csm_key then
  261. advmarkers.import(message:sub(#csm_key + 1), true)
  262. return true
  263. end
  264. end)
  265. run_remote_command()