registration.lua 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. --[[
  2. Advanced Portals (advanced_portals), A minetest add-on that allows certain users to create and place portals on their minetest world.
  3. Copyright (c) 2020 Genshin <emperor_genshin@hotmail.com>
  4. License: GPLv3
  5. --]]
  6. minetest.register_privilege("portals_admin", {"Grants the ability to configure any portal owned by players", give_to_singleplayer = false})
  7. minetest.register_privilege("portals", {"Grants the ability to place and configure their own portals", give_to_singleplayer = false})
  8. local storage = minetest.get_mod_storage()
  9. local footer = "Version 1.0 - By: Genshin"
  10. local from_node_metadata = {}
  11. local previous_input_data = {} --Previous Input Data stored to be reused during active portal placement on the map via on_construct
  12. local saved_input_data = {} --Previous Input Data stored to be reused when active portal is being right_clicked
  13. local portal_list_changes = {}
  14. local previous_portal_ids = {}
  15. local debug = minetest.settings:get_bool("enable_portals_debug") or false
  16. portal_list_changes = {}
  17. --[Local Function] Convert CSV (String list with commas) to a Table Array
  18. local function convert_csv_to_table_array(s)
  19. local result = {}
  20. s = s .. ','
  21. local t = {}
  22. local fieldstart = 1
  23. repeat
  24. if string.find(s, '^"', fieldstart) then
  25. local a, c
  26. local i = fieldstart
  27. repeat
  28. a, i, c = string.find(s, '"("?)', i+1)
  29. until c ~= '"'
  30. if not i then return result end
  31. local f = string.sub(s, fieldstart+1, i-1)
  32. table.insert(t, (string.gsub(f, '""', '"')))
  33. fieldstart = string.find(s, ',', i) + 1
  34. else
  35. local nexti = string.find(s, ',', fieldstart)
  36. table.insert(t, string.sub(s, fieldstart, nexti-1))
  37. fieldstart = nexti + 1
  38. end
  39. until fieldstart > string.len(s)
  40. for k,v in pairs(t) do
  41. result[#result+1] = string.gsub(v, "%s+", "")
  42. end
  43. return result
  44. end
  45. --[Local Function] Convert CSV (Comma separated values) to XYZ Coordinates
  46. local function convert_csv_to_xyz(s)
  47. local result = {}
  48. local cancel = false
  49. s = s .. ','
  50. local t = {}
  51. local fieldstart = 1
  52. repeat
  53. if string.find(s, '^"', fieldstart) then
  54. local a, c
  55. local i = fieldstart
  56. repeat
  57. a, i, c = string.find(s, '"("?)', i+1)
  58. until c ~= '"'
  59. if not i then return result end
  60. local f = string.sub(s, fieldstart+1, i-1)
  61. table.insert(t, (string.gsub(f, '""', '"')))
  62. fieldstart = string.find(s, ',', i) + 1
  63. else
  64. local nexti = string.find(s, ',', fieldstart)
  65. table.insert(t, string.sub(s, fieldstart, nexti-1))
  66. fieldstart = nexti + 1
  67. end
  68. until fieldstart > string.len(s)
  69. --Convert Table into Coordinates
  70. for k,v in pairs(t) do
  71. local number = k
  72. local value = string.gsub(v, "%s+", "")
  73. if string.match(v, "%d+") then
  74. if number == 1 then
  75. result["x"] = tonumber(value)
  76. elseif number == 2 then
  77. result["y"] = tonumber(value)
  78. elseif number == 3 then
  79. result["z"] = tonumber(value)
  80. elseif number > 3 then
  81. break
  82. end
  83. else
  84. cancel = true
  85. end
  86. end
  87. if cancel == true then
  88. return {}
  89. end
  90. return result
  91. end
  92. --[Local Function] Show Portal Configuration Menu
  93. local function show_portal_configuration_menu(name, meta)
  94. if minetest.check_player_privs(name, {portals_admin=true}) or minetest.check_player_privs(name, {portals=true}) then
  95. local owner = meta:get_string("owner")
  96. --Debug Stuff to see and verify if local owner is being called
  97. if debug == true then
  98. minetest.chat_send_player(name, "<[DEBUG]: Owner = "..tostring(owner)..">")
  99. print("<[DEBUG]: Owner = "..tostring(owner)..">")
  100. end
  101. if name ~= owner or (name ~= owner and minetest.check_player_privs(name, {portals_admin=false})) then
  102. minetest.chat_send_player(name, "You are not authorized to configure this portal!")
  103. goto end_portal_config
  104. elseif name == owner or (name ~= owner and minetest.check_player_privs(name, {portals_admin=true})) then
  105. goto show_portals_form
  106. end
  107. ::show_portals_form::
  108. --Load Previous Inputs
  109. local status = meta:get_string("status")
  110. local facedir = meta:get_string("facedir")
  111. local portal_name = meta:get_string("portal_name")
  112. local portal_nametag = meta:get_string("nametag")
  113. local node = meta:get_string("portal_node_name")
  114. local linked_portal_name = meta:get_string("linked_portal")
  115. local portal_pos = minetest.deserialize(meta:get_string("portal_pos"))
  116. local private = meta:get_string("private")
  117. local linked = meta:get_string("linked")
  118. local coordinates = meta:get_string("coordinates")
  119. local whitelist = meta:get_string("whitelist")
  120. local private_int = nil
  121. local linked_int = nil
  122. local status_color = nil
  123. local formspec = nil
  124. from_node_metadata[name] = {
  125. ["nametag"] = portal_nametag,
  126. ["owner"] = owner,
  127. ["status"] = status,
  128. ["facedir"] = facedir,
  129. ["portal_name"] = portal_name,
  130. ["portal_node_name"] = node,
  131. ["portal_pos"] = portal_pos,
  132. ["node"] = node,
  133. ["linked_portal_name"] = linked_portal_name,
  134. ["private"] = private,
  135. ["linked"] = linked,
  136. ["coordinates"] = coordinates,
  137. ["whitelist"] = whitelist
  138. }
  139. --Debug Stuff to see and verify if local metadata strings are being stored in table "from_node_metadata" correctly.
  140. if debug == true then
  141. minetest.chat_send_player(name, "<[DEBUG]: "..tostring(minetest.serialize(from_node_metadata[name]))..">")
  142. print("<[DEBUG]: "..tostring(minetest.serialize(from_node_metadata[name]))..">")
  143. end
  144. if private == "Yes" then
  145. private_int = 1
  146. elseif private == "No" then
  147. private_int = 2
  148. end
  149. if linked == "Yes" then
  150. linked_int = 1
  151. elseif linked == "No" then
  152. linked_int = 2
  153. end
  154. if status == "Inactive" then
  155. status_color = "#e8484a"
  156. elseif status == "Active" then
  157. status_color = "#00f03c"
  158. end
  159. if portal_name == "" then
  160. formspec = {
  161. "size[8,8]",
  162. "background[-0.30,-0.34;8.5,8.5;portals_gui_bg.png;true]",
  163. "image[0,-0.85;3.1,3.5;portals_gui_logo.png]",
  164. "label[3,0.10;Portal Configuration Menu]",
  165. "label[3,0.55;"..minetest.colorize("#cacaca", "Status: ")..minetest.colorize(status_color, status).."]",
  166. "label[3,0.80;"..minetest.colorize("#cacaca", "Owner: ")..minetest.colorize("#fff0a2", owner).."]",
  167. "image[6.60,2.90;0.70,2.43;portals_gui_linked.png]",
  168. "field[1.95,1.90;5.25,0.8;portal_name;"..minetest.colorize("#ff0004", "* ").."Portal ID;"..portal_name.."]",
  169. "field[1.95,2.90;5.25,0.8;set_coords;"..minetest.colorize("#ff0004", "* ").."Destination (If not Linked);"..minetest.formspec_escape(coordinates).."]",
  170. "field[1.95,3.90;4.25,0.8;whitelist; Who else can use this portal? (If Private);"..whitelist.."]",
  171. "label[5.70,3.23;"..minetest.colorize("#ffe44b", "Private?").."]",
  172. "dropdown[5.70,3.59;1,1;private;Yes,No;"..private_int.."]",
  173. "field[1.95,4.90;4.25,0.8;linked_portal;"..minetest.colorize("#ff0004", "* ").."Linked Portal (If Linked);"..linked_portal_name.."]",
  174. "label[5.70,4.22;"..minetest.colorize("#ffe44b", "Linked?").."]",
  175. "dropdown[5.70,4.58;1,1;linked;Yes,No;"..linked_int.."]",
  176. "field[1.95,5.90;4.25,0.8;portal_nametag; Portal Tag;"..portal_nametag.."]",
  177. "image_button_exit[2.5,6.70;1.5,0.8;portals_gui_button.png;apply;Set]",
  178. "image_button_exit[4.0,6.70;1.5,0.8;portals_gui_button.png;cancel;Cancel]",
  179. "label[0,7.90;"..minetest.colorize("#acacac", minetest.formspec_escape(footer)).."]"
  180. }
  181. else
  182. formspec = {
  183. "size[8,8]",
  184. "background[-0.30,-0.34;8.5,8.5;portals_gui_bg.png;true]",
  185. "image[0,-0.85;3.1,3.5;portals_gui_logo.png]",
  186. "label[3,0.10;Portal Configuration Menu]",
  187. "label[3,0.55;"..minetest.colorize("#cacaca", "Status: ")..minetest.colorize(status_color, status).."]",
  188. "label[3,0.80;"..minetest.colorize("#cacaca", "Owner: ")..minetest.colorize("#fff0a2", owner).."]",
  189. "image[6.60,2.90;0.70,2.43;portals_gui_linked.png]",
  190. "label[1.80,1.50;Portal ID]",
  191. "label[1.85,1.80;"..minetest.formspec_escape(portal_name).."]",
  192. "field[1.95,2.90;5.25,0.8;set_coords;"..minetest.colorize("#ff0004", "* ").."Destination (If not Linked);"..minetest.formspec_escape(coordinates).."]",
  193. "field[1.95,3.90;4.25,0.8;whitelist; Who else can use this portal? (If Private);"..whitelist.."]",
  194. "label[5.70,3.23;"..minetest.colorize("#ffe44b", "Private?").."]",
  195. "dropdown[5.70,3.59;1,1;private;Yes,No;"..private_int.."]",
  196. "field[1.95,4.90;4.25,0.8;linked_portal;"..minetest.colorize("#ff0004", "* ").."Linked Portal (If Linked);"..linked_portal_name.."]",
  197. "label[5.70,4.22;"..minetest.colorize("#ffe44b", "Linked?").."]",
  198. "dropdown[5.70,4.58;1,1;linked;Yes,No;"..linked_int.."]",
  199. "field[1.95,5.90;4.25,0.8;portal_nametag; Portal Tag;"..portal_nametag.."]",
  200. "image_button_exit[2.5,6.70;1.5,0.8;portals_gui_button.png;apply;Set]",
  201. "image_button_exit[4.0,6.70;1.5,0.8;portals_gui_button.png;cancel;Cancel]",
  202. "label[0,7.90;"..minetest.colorize("#acacac", minetest.formspec_escape(footer)).."]"
  203. }
  204. end
  205. minetest.show_formspec(name, "portals:configuration_menu", table.concat(formspec, ""))
  206. end
  207. ::end_portal_config::
  208. return
  209. end
  210. --[Function] Get portal data
  211. function get_portal_data(portal)
  212. if not portal then
  213. return nil
  214. end
  215. return minetest.deserialize(storage:get_string(portal))
  216. end
  217. --[Function] Get portal data
  218. function get_portal_data(portal)
  219. if not portal then
  220. return nil
  221. end
  222. return minetest.deserialize(storage:get_string(portal))
  223. end
  224. --[Function] Get portal particle values - TODO
  225. function get_portal_particle_values(portal, valuetype)
  226. local bool = storage:get_string(portal.."_activation_particles") or false
  227. local texture = storage:get_string(portal.."_activation_particle_texture")
  228. local interval = storage:get_string(portal.."_activation_particles_interval")
  229. local radius = storage:get_int(portal.."_activation_particle_spread")
  230. if valuetype == "flag" then
  231. return bool
  232. elseif valuetype == "texture" then
  233. return texture
  234. elseif valuetype == "delay" then
  235. return interval
  236. elseif valuetype == "spread" then
  237. return radius
  238. end
  239. if not portal or not texture or not interval or not radius then
  240. return nil
  241. end
  242. return minetest.deserialize(storage:get_string(portal))
  243. end
  244. function get_portal_handler_origin_height(portal, class)
  245. if class == "warp_handler" then
  246. return storage:get_int(portal.."_warp_handler_height")
  247. elseif class == "nametag" then
  248. return storage:get_int(portal.."_nametag_height")
  249. end
  250. return nil
  251. end
  252. --[Function] Get Portal Handler Sound by type
  253. function get_portal_handler_sounds(portal, soundtype)
  254. local sounds_list = storage:get_string(portal.."_sounds")
  255. local distance = storage:get_int(portal.."_sound_distance")
  256. if sounds_list then
  257. sounds_list = minetest.deserialize(sounds_list)
  258. if soundtype == "portal_activate" then
  259. return sounds_list.activate
  260. elseif soundtype == "portal_ambiance" then
  261. return sounds_list.portal_ambiance
  262. elseif soundtype == "portal_warp" then
  263. return sounds_list.portal_warp
  264. elseif soundtype == "sound_distance" then
  265. return distance
  266. else
  267. return nil
  268. end
  269. return nil
  270. end
  271. return nil
  272. end
  273. --[Function] Register Portal Node
  274. function register_portal(name, def)
  275. --Temporary Data
  276. local groups_data = {}
  277. local tmp = {}
  278. --generate a separate groups table for active portals
  279. if def.groups then
  280. for k,v in pairs(def.groups) do
  281. local group = k
  282. local group_value = v
  283. groups_data[group] = group_value
  284. end
  285. end
  286. local active_portal_groups = groups_data
  287. --Clear temporary data
  288. groups_data = {}
  289. --for active portal nodes, make sure they are hidden away from the inventory so they can be swapped silently, also make sure no one can place it either
  290. active_portal_groups["not_in_creative_inventory"] = 1
  291. --Inactive Portal
  292. minetest.register_node("advanced_portals:"..name.."_inactive",{
  293. description = def.description.." (Inactive)",
  294. drawtype = "mesh",
  295. mesh = def.mesh,
  296. inventory_image = def.inventory_image or nil,
  297. light_source = def.light_source or nil,
  298. tiles = {def.tiles[1], "portal_deactivated.png"},
  299. use_texture_alpha = true,
  300. groups = def.groups,
  301. paramtype2 = "facedir",
  302. selection_box = def.selection_box,
  303. drop = "advanced_portals:"..name.."_inactive",
  304. sounds = def.sounds or nil,
  305. collision_box = def.collision_box,
  306. on_rotate = screwdriver.rotate_simple,
  307. after_place_node = function(pos, placer, itemstack, pointed_thing)
  308. local pname = placer:get_player_name()
  309. if minetest.check_player_privs(pname, {portals_admin = true}) or minetest.check_player_privs(pname, {portals = true}) then
  310. local meta = minetest.get_meta(pos)
  311. local node = minetest.get_node(pos)
  312. meta:set_string("portal_name", tostring(""))
  313. meta:set_string("owner", tostring(pname)) --The owner of the portal
  314. meta:set_string("portal_node_name", name) --The node name of the portal
  315. meta:set_string("facedir", node.param2)
  316. meta:set_string("status", "Inactive")
  317. meta:set_string("facedir", node.param2) --The face direction of the portal, it's needed for swapping nodes
  318. meta:set_string("owner", tostring(pname)) --The owner of the portal
  319. meta:set_string("status", "Inactive") --[Flag] Is the portal active or not?
  320. meta:set_string("portal_pos", minetest.serialize(pos)) --The exact location of the portal
  321. meta:set_string("private", "No") --[Flag] is this portal private accessible?
  322. meta:set_string("linked", "No") ----[Flag] is this portal linked to another portal?
  323. else
  324. minetest.chat_send_player(pname, "**You are not authorized to place portals on the map!")
  325. minetest.remove_node(pos)
  326. return itemstack
  327. end
  328. end,
  329. on_rightclick = function(pos, node, player, itemstack, pointed_thing)
  330. local pname = player:get_player_name()
  331. local meta = minetest.get_meta(pos)
  332. local owner = meta:get_string("owner")
  333. show_portal_configuration_menu(pname, meta)
  334. return itemstack
  335. end
  336. })
  337. --Active Portal
  338. minetest.register_node("advanced_portals:"..name.."_active",{
  339. description = def.description.." (Inactive)\n "..minetest.colorize("#ff0004","*You shouldn't have this!"),
  340. drawtype = "mesh",
  341. mesh = def.mesh,
  342. is_portal = true,
  343. inventory_image = def.inventory_image or nil,
  344. light_source = 30,
  345. tiles = {
  346. def.tiles[1],
  347. {
  348. name = def.activation_sprite,
  349. animation = {
  350. type = "vertical_frames",
  351. aspect_w = 16,
  352. aspect_h = 16,
  353. length = 0.3,
  354. },
  355. backface_culling = true,
  356. },
  357. },
  358. use_texture_alpha = true,
  359. groups = active_portal_groups,
  360. paramtype2 = "facedir",
  361. selection_box = def.selection_box,
  362. drop = "advanced_portals:"..name.."_inactive",
  363. sounds = def.sounds or nil,
  364. collision_box = def.collision_box,
  365. on_rotate = screwdriver.rotate_simple,
  366. on_construct = function(pos)
  367. local meta = minetest.get_meta(pos)
  368. local old_data = previous_input_data
  369. if old_data == nil then else
  370. meta:set_string("portal_name", old_data.portal_name)
  371. meta:set_string("portal_node_name", old_data.portal_node_name)
  372. meta:set_string("facedir", old_data.facedir)
  373. meta:set_string("owner", old_data.owner)
  374. meta:set_string("portal_pos", minetest.serialize(old_data.portal_pos))
  375. meta:set_string("private", old_data.private)
  376. meta:set_string("linked", old_data.linked)
  377. meta:set_string("linked_portal", old_data.linked_portal)
  378. meta:set_string("coordinates", old_data.coordinates)
  379. meta:set_string("whitelist", old_data.whitelist)
  380. meta:set_string("status", old_data.status)
  381. --Debug Stuff to see and verify if local owner is being called
  382. if debug == true then
  383. minetest.chat_send_player(old_data.owner, "<[DEBUG]: Active portal successfully set old metadata at on_construct >")
  384. print("<[DEBUG]: Active portal successfully set old metadata at on_construct >")
  385. end
  386. previous_input_data = nil
  387. end
  388. end,
  389. on_place = function(itemstack, placer, pointed_thing)
  390. local pname = placer:get_player_name()
  391. minetest.log("warning", pname.." attempt to place a illegal node on the map. kicking user "..pname.." from the server due to possible exploitation attempts.")
  392. minetest.chat_send_all(minetest.colorize("#ff8c00", "[EXPLOITATION PREVENTION]:"..pname.." attempted to place unobtainable blocks on the map. kicking user "..pname.." from the server due to possible exploitation attempts."))
  393. minetest.kick_player(pname, "[Exploitation Attempts] Attempt to place unobtainable illegal blocks on the map. This incident will be reported to the server\'s host!")
  394. return
  395. end,
  396. --Clear Data
  397. on_destruct = function(pos)
  398. local meta = minetest.get_meta(pos)
  399. local portal_name = meta:get_string("portal_name")
  400. local portals_list = storage:get_string("portals_list")
  401. storage:set_string(portal_name, "")
  402. if not (portals_list == nil or portals_list == "") then
  403. portals_list = minetest.deserialize(portals_list)
  404. portal_list_changes = portals_list
  405. if portals_list[portal_name] then
  406. portal_list_changes[portal_name] = nil
  407. storage:set_string("portals_list", minetest.serialize(portal_list_changes))
  408. end
  409. portal_list_changes = {}
  410. end
  411. end,
  412. on_rightclick = function(pos, node, player, itemstack, pointed_thing)
  413. local pname = player:get_player_name()
  414. local meta = minetest.get_meta(pos)
  415. local owner = meta:get_string("owner")
  416. local portal_name = meta:get_string("portal_name")
  417. local old_data = saved_input_data[portal_name]
  418. if old_data == nil then else
  419. meta:set_string("nametag", old_data.nametag)
  420. meta:set_string("portal_name", old_data.portal_name)
  421. meta:set_string("portal_node_name", old_data.portal_node_name)
  422. meta:set_string("facedir", old_data.facedir)
  423. meta:set_string("owner", old_data.owner)
  424. meta:set_string("portal_pos", minetest.serialize(old_data.portal_pos))
  425. meta:set_string("private", old_data.private)
  426. meta:set_string("linked", old_data.linked)
  427. meta:set_string("linked_portal", old_data.linked_portal)
  428. meta:set_string("coordinates", old_data.coordinates)
  429. meta:set_string("whitelist", old_data.whitelist)
  430. meta:set_string("status", old_data.status)
  431. saved_input_data[portal_name] = nil
  432. --Debug Stuff to see and verify if local owner is being called
  433. if debug == true then
  434. minetest.chat_send_player(pname, "<[DEBUG]: Active portal successfully set information changes to its metadata via on_rightckick >")
  435. print("<[DEBUG]: Active portal successfully set information changes to its metadata via on_rightckick >")
  436. end
  437. end
  438. show_portal_configuration_menu(pname, meta)
  439. return itemstack
  440. end
  441. })
  442. --Set dynamic intergers
  443. if minetest.registered_nodes["advanced_portals:"..name.."_active"] then
  444. storage:set_int(name.."_warp_handler_height", def.warp_handler_height)
  445. storage:set_int(name.."_nametag_height", def.nametag_height)
  446. storage:set_int(name.."_teleport_radius", def.teleport_radius)
  447. storage:set_int(name.."_warp_height", def.warp_height)
  448. storage:set_string(name.."_sounds", minetest.serialize(def.portal_sounds))
  449. storage:set_int(name.."_sound_distance", def.portal_sound_distance)
  450. storage:set_string(name.."_activation_particles", tostring(def.activation_particles))
  451. storage:set_string(name.."_activation_particle_texture", def.activation_particle_texture)
  452. storage:set_int(name.."_activation_particle_interval", def.activation_particle_interval)
  453. storage:set_int(name.."_activation_particle_spread", def.activation_particle_spread)
  454. return
  455. end
  456. end
  457. --[Fields Handler] Portal Configuration Menu Handler
  458. minetest.register_on_player_receive_fields(function(player, formname, fields)
  459. if formname == "portals:configuration_menu" then
  460. --[Local Function] Update Portal Config
  461. if fields.apply then
  462. goto portal_config_fields
  463. else
  464. return
  465. end
  466. ::portal_config_fields::
  467. local pname = player:get_player_name()
  468. local from_meta = from_node_metadata[pname]
  469. local portal_name = from_meta.portal_name
  470. --Debug Stuff to see and verify if portal name is being called
  471. if debug == true then
  472. minetest.chat_send_all("1) "..tostring(fields.portal_name)..", "..tostring(portal_name))
  473. end
  474. if fields.portal_name and portal_name == "" then
  475. portal_name = fields.portal_name
  476. --Debug Stuff to see and verify if portal name is being called
  477. if debug == true then
  478. minetest.chat_send_all("2) "..tostring(fields.portal_name)..", "..tostring(portal_name))
  479. end
  480. elseif not fields.portal_name and portal_name ~= "" then
  481. fields.portal_name = portal_name
  482. --Debug Stuff to see and verify if portal name is being called
  483. if debug == true then
  484. minetest.chat_send_all("3) "..tostring(fields.portal_name)..", "..tostring(portal_name))
  485. end
  486. end
  487. if (fields.portal_name == nil or fields.portal_name == "") and portal_name == "" then
  488. --Debug Stuff to see and verify if portal name is being called
  489. if debug == true then
  490. minetest.chat_send_all(tostring(fields.portal_name)..", "..tostring(portal_name))
  491. end
  492. minetest.chat_send_player(pname, "**You must give a ID to this portal!")
  493. return
  494. else
  495. local dir = from_meta.facedir
  496. local node = tostring(from_meta.portal_node_name)
  497. local owner = tostring(from_meta.owner)
  498. local status = tostring(from_meta.status)
  499. local portal_pos = from_meta.portal_pos
  500. local private_int = nil
  501. local linked_int = nil
  502. local is_private = nil
  503. local is_linked = nil
  504. local coordinates = {x = 0, y = 0, z = 0}
  505. local handler_ent = nil
  506. local nametag_ent = nil
  507. local handler = nil
  508. local nametag = nil
  509. local portal_data = storage:get_string(fields.portal_name)
  510. local portals_list = storage:get_string("portals_list")
  511. --Debug Stuff to see and verify if local metadata strings exists in table "from_node_metadata".
  512. if debug == true then
  513. minetest.chat_send_all("<[DEBUG]: "..tostring(minetest.serialize(from_meta))..">")
  514. print("<[DEBUG]: "..tostring(minetest.serialize(from_meta))..">")
  515. end
  516. if portal_data == nil or portal_data == "" then else
  517. portal_data = minetest.deserialize(portal_data)
  518. end
  519. if portals_list == nil or portals_list == "" then
  520. storage:set_string("portals_list", minetest.serialize(portal_list_changes))
  521. --Debug Stuff to see and verify if portal list changes are present
  522. if debug == true then
  523. minetest.chat_send_player(pname, "<[DEBUG]: "..tostring(minetest.serialize(portal_list_changes))..">")
  524. end
  525. else
  526. portals_list = minetest.deserialize(storage:get_string("portals_list"))
  527. portal_list_changes = portals_list
  528. --Debug Stuff to see and verify if portal list changes are present
  529. if debug == true then
  530. minetest.chat_send_player(pname, "<[DEBUG]: "..tostring(minetest.serialize(portal_list_changes))..">")
  531. end
  532. end
  533. --update previous portal name and clear it's old data if name does not match with the old portal name
  534. if portal_name == nil or portal_name == "" then
  535. elseif portal_name ~= fields.portal_name then
  536. storage:set_string(portal_name, "")
  537. --clear old portal name from the list
  538. if portals_list[portal_name] then
  539. portal_list_changes[portal_name] = nil
  540. previous_portal_ids[portal_name] = fields.portal_name
  541. storage:set_string("previous_portal_ids", minetest.serialize(previous_portal_ids))
  542. end
  543. end
  544. --Check if portal ID exists on the list, if it does, thenn cancel activation.
  545. if not portals_list[fields.portal_name] then
  546. portal_list_changes[fields.portal_name] = true
  547. elseif portals_list[fields.portal_name] and portal_name == fields.portal_name then
  548. elseif portals_list[fields.portal_name] then
  549. minetest.chat_send_player(pname, "**Unable to change Portal\'s ID, Portal ID \""..fields.portal_name.."\" already exist!")
  550. return
  551. end
  552. if fields.private == "Yes" then
  553. private_int = 1
  554. is_private = true
  555. elseif fields.private == "No" then
  556. private_int = 2
  557. is_private = false
  558. end
  559. if fields.linked == "Yes" then
  560. linked_int = 1
  561. is_linked = true
  562. elseif fields.linked == "No" then
  563. linked_int = 2
  564. is_linked = false
  565. end
  566. if is_linked == false then
  567. coordinates = convert_csv_to_xyz(fields.set_coords)
  568. if next(coordinates) == nil then
  569. minetest.chat_send_player(pname, "**You must enter proper XYZ coordinates for the destination!")
  570. return
  571. end
  572. elseif is_linked == true then
  573. local linked_portal_data = storage:get_string(fields.linked_portal)
  574. --Debug Stuff to see and verify if linked portal data from mod storage has successfully loaded
  575. if debug == true then
  576. minetest.chat_send_player(pname, "<[DEBUG]: Linked Portal Data = "..tostring(linked_portal_data).." >")
  577. print("<[DEBUG]: Linked Portal Data = "..tostring(linked_portal_data).." >")
  578. end
  579. if linked_portal_data == nil or linked_portal_data == "" then
  580. minetest.chat_send_player(pname, "**You must type a existing portal in order to link this portal with another one!")
  581. return
  582. else
  583. linked_portal_data = minetest.deserialize(linked_portal_data)
  584. local target_portal = linked_portal_data["portal_name"]
  585. if target_portal ~= fields.linked_portal then
  586. minetest.chat_send_player(pname, "**You must type a existing portal in order to link this portal with another one!")
  587. return
  588. elseif target_portal == fields.portal_name then
  589. minetest.chat_send_player(pname, "**You can\'t link a portal to itself!")
  590. return
  591. end
  592. end
  593. end
  594. local whitelist = convert_csv_to_table_array(fields.whitelist)
  595. --Debug Stuff to see and verify if local metadata strings are being stored in table "from_node_metadata" correctly.
  596. if debug == true then
  597. minetest.chat_send_player(pname, "<[DEBUG]: "..fields.private..", "..fields.linked..", "..tostring(coordinates)..", "..tostring(whitelist)..">")
  598. minetest.chat_send_player(pname, "<[DEBUG]: "..tostring(minetest.serialize(portal_list_changes))..">")
  599. print("<[DEBUG]: "..fields.private..", "..fields.linked..", "..tostring(coordinates)..", "..tostring(whitelist)..">")
  600. print("<[DEBUG]: "..tostring(minetest.serialize(portal_list_changes))..">")
  601. end
  602. storage:set_string("portals_list", minetest.serialize(portal_list_changes))
  603. --Store data so portal entity can use it to do it's thing
  604. storage:set_string(fields.portal_name, minetest.serialize({
  605. ["nametag"] = fields.portal_nametag or "",
  606. ["portal_name"] = fields.portal_name,
  607. ["facedir"] = dir,
  608. ["portal_node_name"] = node,
  609. ["location"] = portal_pos,
  610. ["owner"] = owner,
  611. ["private"] = is_private,
  612. ["linked"] = is_linked,
  613. ["linked_portal"] = fields.linked_portal,
  614. ["coordinates"] = coordinates,
  615. ["status"] = "Active",
  616. ["whitelist"] = whitelist,
  617. }))
  618. --Stored Context Data for later use
  619. previous_input_data = {
  620. ["nametag"] = fields.portal_nametag or "",
  621. ["portal_name"] = fields.portal_name,
  622. ["facedir"] = dir,
  623. ["portal_node_name"] = node,
  624. ["owner"] = owner,
  625. ["portal_pos"] = portal_pos,
  626. ["private"] = fields.private,
  627. ["linked"] = fields.linked,
  628. ["linked_portal"] = fields.linked_portal,
  629. ["coordinates"] = fields.set_coords,
  630. ["whitelist"] = fields.whitelist,
  631. ["status"] = "Active"
  632. }
  633. saved_input_data[fields.portal_name] = {
  634. ["nametag"] = fields.portal_nametag or "",
  635. ["portal_name"] = fields.portal_name,
  636. ["facedir"] = dir,
  637. ["portal_node_name"] = node,
  638. ["owner"] = owner,
  639. ["portal_pos"] = portal_pos,
  640. ["private"] = fields.private,
  641. ["linked"] = fields.linked,
  642. ["linked_portal"] = fields.linked_portal,
  643. ["coordinates"] = fields.set_coords,
  644. ["whitelist"] = fields.whitelist,
  645. ["status"] = "Active"
  646. }
  647. --minetest.chat_send_player(pname, "<[DEBUG] "..tostring(storage:get_string(fields.portal_name)..">"))
  648. if status == "Inactive" then
  649. local portal_sounds = storage:get_string(node.."_sounds")
  650. sound_distance = storage:get_string(node.."_sounds_distance") or 20
  651. local sound = ""
  652. --Debug Stuff to see and verify if portal sounds list are being called
  653. if debug == true then
  654. minetest.chat_send_player(pname, "<[DEBUG] "..tostring(portal_sounds)..">")
  655. end
  656. if portal_sounds then
  657. portal_sounds = minetest.deserialize(portal_sounds)
  658. sound = portal_sounds.portal_activate
  659. --Debug Stuff to see and verify if portal activation sound is being called
  660. if debug == true then
  661. minetest.chat_send_player(pname, "<[DEBUG] "..tostring(portal_sounds.portal_activate)..">")
  662. end
  663. end
  664. minetest.sound_play(sound, {
  665. pos = portal_pos,
  666. max_hear_distance = sound_distance,
  667. gain = 10.0,
  668. })
  669. --The entity that will handle teleportation behavior
  670. handler_ent = minetest.add_entity({x = portal_pos.x, y = portal_pos.y + storage:get_int(node.."_warp_handler_height"), z = portal_pos.z}, "advanced_portals:portal_handler")
  671. handler = handler_ent:get_luaentity()
  672. handler.portal_owner = owner
  673. handler.scan_radius = storage:get_int(node.."_teleport_radius")
  674. handler.origin_height = storage:get_int(node.."_warp_handler_height")
  675. handler.portal = fields.portal_name
  676. handler.portal_node = tostring("advanced_portals:"..node.."_active")
  677. handler.portal_nodename = node
  678. handler.emit_particles = storage:get_string(node.."_activation_particles")
  679. --The enitiy that acts as the portal's nametag
  680. nametag_ent = minetest.add_entity({x = portal_pos.x, y = portal_pos.y + storage:get_int(node.."_nametag_height"), z = portal_pos.z}, "advanced_portals:portal_nametag")
  681. nametag = nametag_ent:get_luaentity()
  682. nametag.origin_height = storage:get_int(node.."_nametag_height")
  683. nametag.portal = fields.portal_name
  684. nametag.portal_node = tostring("advanced_portals:"..node.."_active")
  685. nametag.portal_nodename = node
  686. nametag.tag = fields.portal_nametag
  687. --Swaps node to make it look like if it activated
  688. minetest.set_node(portal_pos, {name="advanced_portals:"..node.."_active", param2 = dir})
  689. end
  690. minetest.chat_send_player(pname, "**Portal \""..fields.portal_name.."\" configuration has been successfully updated!")
  691. from_node_metadata[pname] = {}
  692. return
  693. end
  694. portal_list_changes = {}
  695. previous_portal_ids = {}
  696. from_node_metadata[pname] = {}
  697. end
  698. end)
  699. --Clear data
  700. minetest.register_on_leaveplayer(function(player)
  701. local name = player:get_player_name()
  702. if from_node_metadata[name] then
  703. from_node_metadata[name] = nil
  704. end
  705. end)