init.lua 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  1. if not minetest.global_exists("marker") then marker = {} end
  2. marker.modpath = minetest.get_modpath("marker")
  3. marker.players = marker.players or {}
  4. marker.gui = marker.gui or {}
  5. marker.steptime = 1
  6. marker.max_waypoints = 100
  7. marker.max_lists = 50
  8. -- Max distance of players who wish to share marker lists.
  9. marker.proximity_range = 10
  10. -- Min/max ranges for visual particles.
  11. marker.pr_min = 40
  12. marker.pr_max = 50
  13. -- Min/max ranges for HUD text objects.
  14. marker.ht_min = 12
  15. marker.ht_max1 = 100
  16. marker.ht_max2 = 110
  17. -- Localize for performance.
  18. local vector_distance = vector.distance
  19. local vector_round = vector.round
  20. local timer = 0
  21. local delay = marker.steptime
  22. function marker.on_globalstep(dtime)
  23. timer = timer + dtime
  24. if timer < delay then return end
  25. timer = 0
  26. marker.update_huds()
  27. end
  28. function marker.update_huds()
  29. -- localize
  30. -- create gui for player if not created already
  31. local players = marker.players
  32. for k, v in pairs(players) do
  33. marker.update_single_hud(k)
  34. end
  35. end
  36. function marker.on_leaveplayer(pref, timeout)
  37. local pname = pref:get_player_name()
  38. marker.players[pname] = nil
  39. marker.gui[pname] = nil
  40. end
  41. function marker.update_single_hud(player)
  42. local allgui = marker.gui
  43. if not allgui[player] then
  44. marker.create_gui(player)
  45. end
  46. local gui = allgui[player]
  47. local waypoints = gui.waypoints
  48. local pref = minetest.get_player_by_name(player)
  49. if not pref or not pref:is_player() then
  50. return
  51. end
  52. local d = vector.distance
  53. local p2 = pref:get_pos()
  54. for i = 1, #waypoints, 1 do
  55. local data = waypoints[i]
  56. local dist = d(data.pos, p2)
  57. if dist > marker.ht_min and dist < marker.ht_max1 then
  58. -- add hud element if nearby and not already added
  59. if not data.hnd then
  60. local wp = vector.add(data.pos, {x=0, y=1, z=0})
  61. local number = "7902626"
  62. if data.highlight then
  63. number = "8739932"
  64. end
  65. data.hnd = pref:hud_add({
  66. hud_elem_type = "waypoint",
  67. name = "Marker",
  68. number = number,
  69. world_pos = wp,
  70. })
  71. end
  72. elseif dist < marker.ht_min or dist > marker.ht_max2 then
  73. -- remove hud element if too far and not yet removed
  74. if data.hnd then
  75. pref:hud_remove(data.hnd)
  76. data.hnd = nil
  77. end
  78. end
  79. if dist < marker.pr_min then
  80. if not data.pts then
  81. local wp = vector.add(data.pos, {x=0, y=0.5, z=0})
  82. local particles = {
  83. amount = 20,
  84. time = 0,
  85. minpos = vector.add(wp, {x=-0.1, y=-0.1, z=-0.1}),
  86. maxpos = vector.add(wp, {x=0.1, y=0.1, z=0.1}),
  87. minvel = vector.new(-0.5, -0.5, -0.5),
  88. maxvel = vector.new(0.5, 0.5, 0.5),
  89. minacc = {x=0, y=0, z=0},
  90. maxacc = {x=0, y=0, z=0},
  91. minexptime = 0.5,
  92. maxexptime = 2.0,
  93. minsize = 1,
  94. maxsize = 1,
  95. collisiondetection = false,
  96. collision_removal = false,
  97. vertical = false,
  98. texture = "quartz_crystal_piece.png",
  99. glow = 14,
  100. playername = player,
  101. }
  102. data.pts = minetest.add_particlespawner_single(particles)
  103. end
  104. elseif dist > marker.pr_max then
  105. if data.pts then
  106. minetest.delete_particlespawner(data.pts, player)
  107. data.pts = nil
  108. end
  109. end
  110. end
  111. end
  112. function marker.update_hud_markers(player, list, highlight)
  113. -- localize
  114. local allgui = marker.gui
  115. if not allgui[player] then
  116. marker.create_gui(player)
  117. end
  118. local gui = allgui[player]
  119. local waypoints = gui.waypoints
  120. -- remove all existing hud elements
  121. local pref = minetest.get_player_by_name(player)
  122. if not pref or not pref:is_player() then
  123. return
  124. end
  125. for i = 1, #waypoints, 1 do
  126. local data = waypoints[i]
  127. if data.hnd then
  128. pref:hud_remove(data.hnd)
  129. data.hnd = nil
  130. end
  131. if data.pts then
  132. minetest.delete_particlespawner(data.pts, player)
  133. data.pts = nil
  134. end
  135. if data.highlight then
  136. data.highlight = nil
  137. end
  138. end
  139. -- update waypoint list data
  140. gui.waypoints = {}
  141. waypoints = gui.waypoints
  142. if gui.show and list and list ~= "" then
  143. local data = marker.get_list(player, list)
  144. for i = 1, #data, 1 do
  145. waypoints[#waypoints + 1] = {pos=table.copy(data[i])}
  146. if highlight and highlight == i then
  147. waypoints[#waypoints].highlight = true
  148. end
  149. end
  150. end
  151. marker.update_single_hud(player)
  152. end
  153. function marker.highlight_marker(player, index)
  154. -- localize
  155. local allgui = marker.gui
  156. if not allgui[player] then
  157. marker.create_gui(player)
  158. end
  159. local gui = allgui[player]
  160. local waypoints = gui.waypoints
  161. for i = 1, #waypoints, 1 do
  162. local data = waypoints[i]
  163. if data.highlight then
  164. data.highlight = nil
  165. -- return marker to regular color
  166. if data.hnd then
  167. local pref = minetest.get_player_by_name(player)
  168. if pref and pref:is_player() then
  169. pref:hud_change(data.hnd, "number", "7902626")
  170. end
  171. end
  172. end
  173. end
  174. if index >= 1 and index <= #waypoints then
  175. local data = waypoints[index]
  176. if not data.highlight then
  177. data.highlight = true
  178. -- change (highlight) marker color
  179. if data.hnd then
  180. local pref = minetest.get_player_by_name(player)
  181. if pref and pref:is_player() then
  182. pref:hud_change(data.hnd, "number", "8739932")
  183. end
  184. end
  185. end
  186. end
  187. end
  188. -- private: load player data
  189. function marker.load_player(player)
  190. -- localize
  191. local players = marker.players
  192. -- load player data from mod storage
  193. local str = marker.storage:get_string(player)
  194. assert(type(str) == "string")
  195. if str == "" then
  196. players[player] = {}
  197. return
  198. end
  199. local lists = minetest.deserialize(str)
  200. if not lists then
  201. players[player] = {}
  202. return
  203. end
  204. -- data is now loaded
  205. assert(type(lists) == "table")
  206. players[player] = lists
  207. end
  208. -- private: save player data
  209. function marker.save_player(player)
  210. -- localize
  211. local players = marker.players
  212. -- localize
  213. local lists = players[player] or {}
  214. local str = minetest.serialize(lists)
  215. assert(type(str) == "string")
  216. -- send data to mod storage
  217. marker.storage:set_string(player, str)
  218. end
  219. -- api: player name, position, list name
  220. function marker.add_waypoint(player, pos, list)
  221. -- localize
  222. local players = marker.players
  223. -- load data for player if not loaded already
  224. if not players[player] then
  225. marker.load_player(player)
  226. end
  227. -- localize
  228. local lists = players[player]
  229. -- create waypoint list if not created already
  230. local found = false
  231. local positions
  232. for i = 1, #lists, 1 do
  233. if lists[i].name == list then
  234. found = true
  235. positions = lists[i].data
  236. break
  237. end
  238. end
  239. if not found then
  240. lists[#lists + 1] = {name=list, data={}}
  241. positions = lists[#lists].data
  242. end
  243. -- add position
  244. positions[#positions + 1] = vector_round(pos)
  245. -- save changes
  246. marker.save_player(player)
  247. end
  248. -- api: player name, index, list name
  249. function marker.remove_waypoint(player, index, list)
  250. -- localize
  251. local players = marker.players
  252. -- load data for player if not loaded already
  253. if not players[player] then
  254. marker.load_player(player)
  255. end
  256. -- localize
  257. local lists = players[player]
  258. -- search player data for named list
  259. -- ignore if list doesn't exist
  260. local found = false
  261. local positions
  262. for i = 1, #lists, 1 do
  263. if lists[i].name == list then
  264. found = true
  265. positions = lists[i].data
  266. break
  267. end
  268. end
  269. if not found then
  270. return
  271. end
  272. -- localize
  273. local equals = vector.equals
  274. local changed = false
  275. local pos
  276. -- erase position from positions list
  277. if index >= 1 and index <= #positions then
  278. pos = positions[index]
  279. table.remove(positions, index)
  280. changed = true
  281. end
  282. -- save changes if needed
  283. if changed then
  284. marker.save_player(player)
  285. end
  286. -- return pos or nil
  287. return pos
  288. end
  289. -- private: get the list of positions for a given list name
  290. --
  291. -- important: the list can be modified, and if modified, must be saved afterward
  292. -- by calling marker.save_player(), otherwise changes will be lost eventually
  293. function marker.get_list(player, list)
  294. assert(type(list) == "string" and list ~= "")
  295. -- localize
  296. local players = marker.players
  297. -- load data for player if not loaded already
  298. if not players[player] then
  299. marker.load_player(player)
  300. end
  301. -- localize
  302. local lists = players[player]
  303. -- if named list doesn't exist, create it
  304. local found = false
  305. local positions
  306. for i = 1, #lists, 1 do
  307. if lists[i].name == list then
  308. found = true
  309. positions = lists[i].data
  310. break
  311. end
  312. end
  313. if not found then
  314. lists[#lists + 1] = {name=list, data={}}
  315. positions = lists[#lists].data
  316. -- a new list was added, need to save data
  317. marker.save_player(player)
  318. end
  319. -- return the named list
  320. -- the list can be modifed
  321. -- remember to save the data
  322. return positions
  323. end
  324. -- api: player name, list index
  325. function marker.get_list_name(player, index)
  326. -- localize
  327. local players = marker.players
  328. -- load data for player if not loaded already
  329. if not players[player] then
  330. marker.load_player(player)
  331. end
  332. -- localize
  333. local lists = players[player]
  334. local name = ""
  335. for i = 1, #lists, 1 do
  336. if lists[i].name == "default" then
  337. if index >= i then
  338. index = index + 1
  339. -- fix corner case
  340. if index > #lists then
  341. index = #lists
  342. end
  343. end
  344. end
  345. if i == index then
  346. name = lists[i].name
  347. break
  348. end
  349. end
  350. return name
  351. end
  352. -- api: player name, list name
  353. function marker.have_list(player, list)
  354. local players = marker.players
  355. if not players[player] then
  356. return false
  357. end
  358. local alists = players[player]
  359. local found = false
  360. for i = 1, #alists, 1 do
  361. if alists[i].name == list then
  362. found = true
  363. break
  364. end
  365. end
  366. return found
  367. end
  368. -- api: player name, list name
  369. function marker.list_size(player, list)
  370. local players = marker.players
  371. if not players[player] then
  372. return 0
  373. end
  374. local alists = players[player]
  375. local size = 0
  376. for i = 1, #alists, 1 do
  377. if alists[i].name == list then
  378. size = #(alists[i].data)
  379. break
  380. end
  381. end
  382. return size
  383. end
  384. function marker.list_count(player)
  385. local players = marker.players
  386. if not players[player] then
  387. return 0
  388. end
  389. local alists = players[player]
  390. local size = 0
  391. for i = 1, #alists, 1 do
  392. if alists[i].name ~= "default" then
  393. size = size + 1
  394. end
  395. end
  396. return size
  397. end
  398. -- api: player name, list name
  399. function marker.remove_list(player, list)
  400. local players = marker.players
  401. if not players[player] then
  402. return
  403. end
  404. local changed = false
  405. local alists = players[player]
  406. for i = 1, #alists, 1 do
  407. if alists[i].name == list then
  408. table.remove(alists, i)
  409. changed = true
  410. break
  411. end
  412. end
  413. if changed then
  414. marker.save_player(player)
  415. end
  416. end
  417. -- private: create gui-data for player
  418. function marker.create_gui(player)
  419. marker.gui[player] = {
  420. index1 = -1,
  421. index2 = -1,
  422. index3 = -1,
  423. listname = "",
  424. playername = "",
  425. waypoints = {},
  426. }
  427. end
  428. -- private: assemble a formspec string
  429. function marker.get_formspec(player)
  430. -- localize
  431. local players = marker.players
  432. local allgui = marker.gui
  433. -- load data for player if not loaded already
  434. if not players[player] then
  435. marker.load_player(player)
  436. end
  437. local alists = players[player]
  438. -- create gui for player if not created already
  439. if not allgui[player] then
  440. marker.create_gui(player)
  441. end
  442. local gui = allgui[player]
  443. local deflist = marker.get_list(player, "default")
  444. local formspec = "size[9,7]" ..
  445. default.gui_bg ..
  446. default.gui_bg_img ..
  447. default.gui_slots
  448. formspec = formspec ..
  449. "real_coordinates[true]" ..
  450. "container[0,0]" ..
  451. "item_image[0.35,0.34;1,1;passport:passport_adv]" ..
  452. "label[1.5,0.5;Key Device Marker System]" ..
  453. "container_end[]" ..
  454. "container[0.1,0.85]" ..
  455. "field[0.3,1.0;2.5,0.7;listname;;" .. minetest.formspec_escape(gui.listname) .. "]" ..
  456. "field[0.3,1.85;2.5,0.7;player;;" .. minetest.formspec_escape(gui.playername) .. "]" ..
  457. "button[3.0,1.0;0.9,0.7;addlist;>]" ..
  458. "button[4.1,1.0;0.9,0.7;dellist;X]" ..
  459. "button[3.0,1.85;2,0.7;sendlist;Send To Key]" ..
  460. "button[5.2,1.0;1.1,0.7;import;Import]" ..
  461. "button[5.2,1.85;1.1,0.7;export;Export]" ..
  462. "container_end[]"
  463. -- List names: top right.
  464. formspec = formspec ..
  465. "textlist[6.7,0.4;4.5,3.0;lists;"
  466. local comma = ""
  467. --minetest.log(dump(alists))
  468. for i = 1, #alists, 1 do
  469. local k = alists[i].name
  470. if k ~= "default" then
  471. formspec = formspec .. comma .. minetest.formspec_escape(k)
  472. comma = ","
  473. end
  474. end
  475. -- Loose markers: bottom left.
  476. formspec = formspec .. ";" .. gui.index1 .. "]" ..
  477. "textlist[0.4,3.9;4.7,3.1;markers;"
  478. for i = 1, #deflist, 1 do
  479. local s = rc.pos_to_namestr(deflist[i])
  480. formspec = formspec .. minetest.formspec_escape(s)
  481. if i < #deflist then
  482. formspec = formspec .. ","
  483. end
  484. end
  485. -- List positions: bottom right.
  486. formspec = formspec .. ";" .. gui.index2 .. "]" ..
  487. "textlist[6.7,3.9;4.5,4.55;positions;"
  488. local lname = marker.get_list_name(player, gui.index1)
  489. if lname and lname ~= "" then
  490. local data = marker.get_list(player, lname)
  491. comma = ""
  492. for i = 1, #data, 1 do
  493. local s = minetest.formspec_escape(rc.pos_to_namestr(data[i]))
  494. formspec = formspec .. comma .. s
  495. comma = ","
  496. end
  497. end
  498. formspec = formspec .. ";" .. gui.index3 .. "]"
  499. formspec = formspec ..
  500. "container[1.4,0.9]" ..
  501. "button[4.0,3.0;1,0.7;ls;<]" ..
  502. "button[4.0,3.8;1,0.7;mark;Mark]" ..
  503. "button[4.0,4.6;1,0.7;delete;Erase]" ..
  504. "button[4.0,5.4;1,0.7;rs;>]" ..
  505. "container_end[]" ..
  506. "container[0.4,1.5]" ..
  507. "button[0.0,6.25;1.2,0.7;done;Done]"
  508. if not gui.show then
  509. formspec = formspec .. "button[2.8,6.25;3.2,0.7;show;Enable Scan]"
  510. else
  511. formspec = formspec .. "button[2.8,6.25;3.2,0.7;hide;Disable Scan]"
  512. end
  513. formspec = formspec .. "container_end[]"
  514. return formspec
  515. end
  516. -- api: show formspec to player
  517. function marker.show_formspec(player)
  518. local formspec = marker.get_formspec(player)
  519. minetest.show_formspec(player, "marker:fs", formspec)
  520. end
  521. marker.on_receive_fields = function(player, formname, fields)
  522. if formname ~= "marker:fs" then return end
  523. local pname = player:get_player_name()
  524. -- security check to make sure player can use this feature
  525. local inv = player:get_inventory()
  526. if not inv:contains_item("main", "passport:passport_adv") then
  527. return true
  528. end
  529. -- ensure user-data is loaded and available
  530. if not marker.players[pname] then
  531. marker.load_player(pname)
  532. end
  533. -- localize
  534. -- create gui for player if not created already
  535. local allgui = marker.gui
  536. if not allgui[pname] then
  537. marker.create_gui(pname)
  538. end
  539. local gui = allgui[pname]
  540. if fields.done then
  541. passport.show_formspec(pname)
  542. return true
  543. end
  544. if fields.quit then
  545. return true
  546. end
  547. if fields.listname then
  548. gui.listname = fields.listname
  549. end
  550. if fields.player then
  551. gui.playername = fields.player
  552. end
  553. if fields.show then
  554. gui.show = true
  555. if gui.listname and gui.listname ~= "" then
  556. marker.update_hud_markers(pname, gui.listname)
  557. end
  558. end
  559. if fields.hide then
  560. gui.show = nil
  561. marker.update_hud_markers(pname)
  562. end
  563. if fields.addlist then
  564. if marker.list_count(pname) < marker.max_lists then
  565. local name = fields.listname or ""
  566. name = name:trim()
  567. if name == "" then
  568. minetest.chat_send_player(pname, "# Server: Cannot add list with empty name.")
  569. elseif name == "default" then
  570. minetest.chat_send_player(pname, "# Server: Cannot add list with reserved name.")
  571. else
  572. if marker.have_list(pname, name) then
  573. minetest.chat_send_player(pname, "# Server: Cannot add list, it already exists.")
  574. else
  575. -- this automatically adds the list if it doesn't exist
  576. local pos = player:get_pos()
  577. -- No longer needed since player position is now their center instead
  578. -- of their feet.
  579. --pos.y = pos.y + 1
  580. marker.add_waypoint(pname, pos, name)
  581. gui.index1 = #(marker.players[pname])
  582. gui.index3 = -1
  583. marker.update_hud_markers(pname, name)
  584. minetest.chat_send_player(pname, "# Server: Added list.")
  585. end
  586. end
  587. else
  588. minetest.chat_send_player(pname, "# Server: Marker list roster is full, cannot create a new marker list.")
  589. end
  590. elseif fields.dellist then
  591. local name = fields.listname or ""
  592. name = name:trim()
  593. if name == "" then
  594. minetest.chat_send_player(pname, "# Server: Cannot remove list with empty name.")
  595. elseif name == "default" then
  596. minetest.chat_send_player(pname, "# Server: Cannot remove list with reserved name.")
  597. else
  598. if marker.have_list(pname, name) then
  599. marker.remove_list(pname, name)
  600. gui.index1 = -1
  601. gui.listname = ""
  602. marker.update_hud_markers(pname)
  603. minetest.chat_send_player(pname, "# Server: Removed marker list.")
  604. else
  605. minetest.chat_send_player(pname, "# Server: Cannot remove non-existent list.")
  606. end
  607. end
  608. elseif fields.export then
  609. local name = fields.listname or ""
  610. name = name:trim()
  611. if name == "" then
  612. minetest.chat_send_player(pname, "# Server: Cannot export list with empty name.")
  613. else
  614. if marker.have_list(pname, name) then
  615. if inv:contains_item("main", "default:paper") then
  616. local data = marker.get_list(pname, name)
  617. local json = minetest.write_json(data)
  618. if json then
  619. local b64 = minetest.encode_base64(json)
  620. if b64 then
  621. local mname = "# Marker list: \"" .. name .. "\"\n"
  622. -- Split into lines.
  623. local s = mname .. "=== MARKER LIST ===\n"
  624. for k = 1, #b64, 40 do
  625. s = s .. (b64:sub(k, k + 39) .. "\n")
  626. end
  627. s = s .. "=== END OF LIST ==="
  628. local serialized = memorandum.compose_metadata({
  629. text = s,
  630. signed = pname,
  631. })
  632. local itemstack = inv:add_item("main", {
  633. name="memorandum:letter",
  634. count=1, wear=0,
  635. metadata=serialized,
  636. })
  637. if itemstack:is_empty() then
  638. inv:remove_item("main", "default:paper")
  639. minetest.chat_send_player(pname, "# Server: Exported marker list.")
  640. else
  641. minetest.chat_send_player(pname, "# Server: Could not get blank paper.")
  642. end
  643. else
  644. minetest.chat_send_player(pname, "# Server: Base64 encode error.")
  645. end
  646. else
  647. minetest.chat_send_player(pname, "# Server: JSON encode error.")
  648. end
  649. else
  650. minetest.chat_send_player(pname, "# Server: You need blank paper to export a list.")
  651. end
  652. else
  653. minetest.chat_send_player(pname, "# Server: Cannot export non-existent list.")
  654. end
  655. end
  656. elseif fields.import then
  657. if marker.list_count(pname) < marker.max_lists then
  658. local name = fields.listname or ""
  659. name = name:trim()
  660. if name == "" then
  661. minetest.chat_send_player(pname, "# Server: Cannot add list with empty name.")
  662. elseif name == "default" then
  663. minetest.chat_send_player(pname, "# Server: Cannot add list with reserved name.")
  664. else
  665. if marker.have_list(pname, name) then
  666. minetest.chat_send_player(pname, "# Server: Cannot add list, it already exists.")
  667. else
  668. local main = player:get_wield_list()
  669. local idx = player:get_wield_index() + 1
  670. local stack = inv:get_stack(main, idx)
  671. if stack:get_count() == 1 and stack:get_name() == "memorandum:letter" then
  672. -- Deserialize.
  673. local data = memorandum.get_data_from_stack(stack) or {}
  674. local str = data.message or ""
  675. local p1, p2 = str:find("=== MARKER LIST ===")
  676. local p3, p4 = str:find("=== END OF LIST ===")
  677. if p1 and p2 and p3 and p4 then
  678. -- Extract b64 data and strip whitespace.
  679. local b64 = str:sub(p2+1, p3-1):gsub("[ \t\r\f\n]", ""):trim()
  680. local json = minetest.decode_base64(b64)
  681. if json then
  682. -- Warning: this is untrusted data, handle it carefully.
  683. local tfrom = minetest.parse_json(json)
  684. if tfrom and type(tfrom) == "table" then
  685. -- because we checked to make sure the key doesn't have this list already,
  686. -- we have assured that this will create it for the first time, and it will be empty
  687. local newlist = marker.get_list(pname, name)
  688. for i = 1, #tfrom, 1 do
  689. if i <= marker.max_waypoints then
  690. local x = math.round(tonumber(tfrom[i].x) or 0)
  691. local y = math.round(tonumber(tfrom[i].y) or 0)
  692. local z = math.round(tonumber(tfrom[i].z) or 0)
  693. newlist[#newlist + 1] = {x=x,y=y,z=z}
  694. else
  695. break
  696. end
  697. end
  698. marker.save_player(pname)
  699. minetest.chat_send_player(pname, "# Server: Marker list imported.")
  700. else
  701. minetest.chat_send_player(pname, "# Server: JSON decode error.")
  702. end
  703. else
  704. minetest.chat_send_player(pname, "# Server: Base64 decode error.")
  705. end
  706. else
  707. minetest.chat_send_player(pname, "# Server: Could not process memorandum info.")
  708. end
  709. else
  710. minetest.chat_send_player(pname, "# Server: You must put a marker list memorandum next to your Key.")
  711. end
  712. end
  713. end
  714. else
  715. minetest.chat_send_player(pname, "# Server: Marker list roster is full, cannot create a new marker list.")
  716. end
  717. elseif fields.sendlist then
  718. local targetname = fields.player or ""
  719. targetname = targetname:trim()
  720. if targetname ~= "" then
  721. targetname = rename.grn(targetname)
  722. if targetname ~= pname then
  723. local ptarget = minetest.get_player_by_name(targetname)
  724. if ptarget and ptarget:is_player() then
  725. if vector_distance(ptarget:get_pos(), player:get_pos()) < marker.proximity_range then
  726. local inv = ptarget:get_inventory()
  727. if inv:contains_item("main", "passport:passport_adv") then
  728. local name = marker.get_list_name(pname, gui.index1)
  729. if name and name ~= "" then
  730. -- load data for target player if not loaded already
  731. -- otherwise checking to see if player already has list could fail
  732. -- in a very wrong way
  733. if not marker.players[targetname] then
  734. marker.load_player(targetname)
  735. end
  736. if marker.have_list(targetname, name) then
  737. minetest.chat_send_player(pname, "# Server: The other Key already hosts a marker list with that name. Cannot transfer data!")
  738. else
  739. if marker.list_count(targetname) < marker.max_lists then
  740. local datafrom = marker.get_list(pname, name)
  741. -- because we checked to make sure the target key doesn't have this list already,
  742. -- we have assured that this will create it for the first time, and it will be empty
  743. local datato = marker.get_list(targetname, name)
  744. for i = 1, #datafrom, 1 do
  745. datato[#datato + 1] = table.copy(datafrom[i])
  746. end
  747. marker.save_player(targetname)
  748. minetest.chat_send_player(pname, "# Server: Marker list sent!")
  749. minetest.chat_send_player(targetname, "# Server: <" .. rename.gpn(pname) .. "> sent a marker list to your Key!")
  750. else
  751. minetest.chat_send_player(pname, "# Server: The other Key's marker list storage is full! Cannot transfer data.")
  752. end
  753. end
  754. else
  755. minetest.chat_send_player(pname, "# Server: You must select a marker list, first.")
  756. end
  757. else
  758. minetest.chat_send_player(pname, "# Server: The other player does not have a Key!")
  759. end
  760. else
  761. minetest.chat_send_player(pname, "# Server: You need to stand close to the other player's Key to transfer a marker list.")
  762. end
  763. else
  764. minetest.chat_send_player(pname, "# Server: The specified player is not available.")
  765. end
  766. else
  767. minetest.chat_send_player(pname, "# Server: Cannot send marker list to your own Key.")
  768. end
  769. else
  770. minetest.chat_send_player(pname, "# Server: You must specify the name of a player to send a marker list to.")
  771. end
  772. elseif fields.ls then
  773. local name = marker.get_list_name(pname, gui.index1)
  774. if name and name ~= "" then
  775. if marker.list_size(pname, "default") < marker.max_waypoints then
  776. local data = marker.get_list(pname, name)
  777. if gui.index3 >= 1 and gui.index3 <= #data then
  778. local pos = marker.remove_waypoint(pname, gui.index3, name)
  779. if pos then
  780. marker.add_waypoint(pname, pos, "default")
  781. local deflist = marker.get_list(pname, "default")
  782. gui.index2 = #deflist
  783. gui.index3 = -1
  784. marker.update_hud_markers(pname, name)
  785. minetest.chat_send_player(pname, "# Server: Moved marker to free-list.")
  786. else
  787. minetest.chat_send_player(pname, "# Server: Could not remove marker from list.")
  788. end
  789. else
  790. minetest.chat_send_player(pname, "# Server: You need to have selected a marker to be moved.")
  791. end
  792. else
  793. minetest.chat_send_player(pname, "# Server: Cannot remove marker from list, free-list storage is full.")
  794. end
  795. else
  796. minetest.chat_send_player(pname, "# Server: You must select a marker list, first.")
  797. end
  798. elseif fields.rs then
  799. local name = marker.get_list_name(pname, gui.index1)
  800. if name and name ~= "" then
  801. if marker.list_size(pname, name) < marker.max_waypoints then
  802. local data = marker.get_list(pname, "default")
  803. if gui.index2 >= 1 and gui.index2 <= #data then
  804. local pos = marker.remove_waypoint(pname, gui.index2, "default")
  805. if pos then
  806. marker.add_waypoint(pname, pos, name)
  807. local thelist = marker.get_list(pname, name)
  808. gui.index3 = #thelist
  809. gui.index2 = -1
  810. marker.update_hud_markers(pname, name, gui.index3)
  811. minetest.chat_send_player(pname, "# Server: Moved unattached marker to list.")
  812. else
  813. minetest.chat_send_player(pname, "# Server: Could not remove marker from free-list.")
  814. end
  815. else
  816. minetest.chat_send_player(pname, "# Server: You need to have selected a marker to be moved.")
  817. end
  818. else
  819. minetest.chat_send_player(pname, "# Server: Cannot add marker to list, list storage is full.")
  820. end
  821. else
  822. minetest.chat_send_player(pname, "# Server: You must select a marker list, first.")
  823. end
  824. elseif fields.mark then
  825. if marker.list_size(pname, "default") < marker.max_waypoints then
  826. local pos = player:get_pos()
  827. -- No longer needed since player position is their center instead of their feet.
  828. --pos.y = pos.y + 1
  829. marker.add_waypoint(pname, pos, "default")
  830. local deflist = marker.get_list(pname, "default")
  831. gui.index2 = #deflist
  832. minetest.chat_send_player(pname, "# Server: Placed marker at " .. rc.pos_to_namestr(vector_round(pos)) .. ".")
  833. else
  834. minetest.chat_send_player(pname, "# Server: Cannot place a new marker, storage is full.")
  835. end
  836. elseif fields.delete then
  837. local index = gui.index2
  838. local deflist = marker.get_list(pname, "default")
  839. if index >= 1 and index <= #deflist then
  840. local s = rc.pos_to_namestr(deflist[index])
  841. marker.remove_waypoint(pname, index, "default")
  842. gui.index2 = -1
  843. minetest.chat_send_player(pname, "# Server: Removed marker: " .. s .. ".")
  844. else
  845. minetest.chat_send_player(pname, "# Server: You must select a marker from the marker list, to erase it.")
  846. end
  847. elseif fields.lists then
  848. local event = minetest.explode_textlist_event(fields.lists)
  849. if event.type == "CHG" then
  850. local index = event.index
  851. gui.index1 = index
  852. gui.index3 = -1
  853. gui.listname = marker.get_list_name(pname, index)
  854. marker.update_hud_markers(pname, gui.listname)
  855. end
  856. elseif fields.markers then
  857. local event = minetest.explode_textlist_event(fields.markers)
  858. if event.type == "CHG" then
  859. local index = event.index
  860. gui.index2 = index
  861. end
  862. elseif fields.positions then
  863. local event = minetest.explode_textlist_event(fields.positions)
  864. if event.type == "CHG" then
  865. local index = event.index
  866. gui.index3 = index
  867. marker.highlight_marker(pname, index)
  868. end
  869. elseif fields.listname then
  870. -- nothing here atm
  871. elseif fields.player then
  872. -- nothing here atm
  873. end
  874. marker.show_formspec(pname)
  875. return true
  876. end
  877. if not marker.registered then
  878. marker.storage = minetest.get_mod_storage()
  879. minetest.register_on_player_receive_fields(function(...)
  880. return marker.on_receive_fields(...)
  881. end)
  882. minetest.register_globalstep(function(...)
  883. return marker.on_globalstep(...)
  884. end)
  885. minetest.register_on_leaveplayer(function(...)
  886. return marker.on_leaveplayer(...)
  887. end)
  888. local c = "marker:core"
  889. local f = marker.modpath .. "/init.lua"
  890. reload.register_file(c, f, false)
  891. marker.registered = true
  892. end