init.lua 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316
  1. protector = protector or {}
  2. protector.players = protector.players or {}
  3. protector.mod = "redo"
  4. protector.modpath = minetest.get_modpath("protector")
  5. protector.radius = 5
  6. protector.radius_small = 3 -- Must always be smaller than primary radius.
  7. protector.max_share_count = 12
  8. -- Tool drop is disabled because it is too easy to exploit this using lag.
  9. --protector.drop = minetest.setting_getbool("protector_drop") or false
  10. protector.flip = minetest.settings:get_bool("protector_flip") or false
  11. protector.hurt = (tonumber(minetest.settings:get("protector_hurt")) or 0)
  12. protector.display_time = 60*2
  13. dofile(protector.modpath .. "/hud.lua")
  14. dofile(protector.modpath .. "/tool.lua")
  15. -- Temporary pos store.
  16. local player_pos = protector.players
  17. local get_public_time = function()
  18. return os.date("!%Y/%m/%d UTC")
  19. end
  20. -- Use this function ONLY when calling a Minetest API in
  21. -- a situation where protection interferes!
  22. local PROTECTION_ENABLED = true
  23. function protector.enable_protection(enable)
  24. PROTECTION_ENABLED = enable
  25. end
  26. -- Function to determine whether player may place protection in a realm.
  27. -- Admin can always place protection regardless of realm.
  28. local function realm_allows_protection(pos, pname)
  29. if gdac.player_is_admin(pname) then
  30. return true
  31. end
  32. local rn = rc.current_realm_at_pos(pos)
  33. if rn == "abyss" or rn == "" then
  34. return false
  35. end
  36. return true
  37. end
  38. -- Singular function to find relevant protector nodes in an area.
  39. function protector.find_protector_nodes(pos, r, mult, nodename)
  40. -- Arguments:
  41. -- `pos` = the point of interaction.
  42. -- `r` = the primary radius, either 1 or 5. If 1 then we're only looking for the protector itself.
  43. -- `mult` = how much to scale the primary radius by. 1 or 2. 2 is for checking overlaps.
  44. -- `nodename` = the name of the protector calling this function. May be "".
  45. -- The logic is complex.
  46. r = r * mult
  47. local positions, counts = minetest.find_nodes_in_area(
  48. {x = pos.x - r, y = pos.y - r, z = pos.z - r},
  49. {x = pos.x + r, y = pos.y + r, z = pos.z + r},
  50. {"protector:protect", "protector:protect2", "protector:protect3", "protector:protect4"})
  51. local p1 = counts["protector:protect"] or 0
  52. local p2 = counts["protector:protect2"] or 0
  53. -- Does scanned zone contain any smaller protectors?
  54. local p3 = counts["protector:protect3"] or 0
  55. local p4 = counts["protector:protect4"] or 0
  56. if p1 > 0 or p2 > 0 then
  57. -- Determine necessary culling radius based on what we're doing.
  58. local r2 = protector.radius * mult
  59. if mult == 2 and (nodename == "protector:protect3" or nodename == "protector:protect4") then
  60. r2 = protector.radius + protector.radius_small
  61. end
  62. -- Remove irrelevant protectors.
  63. -- These are protectors that are too far away for their protection radius to matter to us.
  64. ::redo::
  65. for i = 1, #positions, 1 do
  66. local p = positions[i]
  67. local nn = minetest.get_node(p).name
  68. if nn == "protector:protect" or nn == "protector:protect2" then
  69. local minp = {x=pos.x-r2, y=pos.y-r2, z=pos.z-r2}
  70. local maxp = {x=pos.x+r2, y=pos.y+r2, z=pos.z+r2}
  71. if p.x > maxp.x or p.x < minp.x or p.y > maxp.y or p.y < minp.y or p.z > maxp.z or p.z < minp.z then
  72. table.remove(positions, i)
  73. goto redo
  74. end
  75. end
  76. end
  77. end
  78. if p3 > 0 or p4 > 0 then
  79. -- Determine necessary culling radius based on what we're doing.
  80. local r2 = protector.radius_small * mult
  81. if mult == 2 and (nodename == "protector:protect" or nodename == "protector:protect2") then
  82. r2 = protector.radius + protector.radius_small
  83. end
  84. -- Remove irrelevant protectors.
  85. -- These are protectors that are too far away for their protection radius to matter to us.
  86. ::redo::
  87. for i = 1, #positions, 1 do
  88. local p = positions[i]
  89. local nn = minetest.get_node(p).name
  90. if nn == "protector:protect3" or nn == "protector:protect4" then
  91. local minp = {x=pos.x-r2, y=pos.y-r2, z=pos.z-r2}
  92. local maxp = {x=pos.x+r2, y=pos.y+r2, z=pos.z+r2}
  93. if p.x > maxp.x or p.x < minp.x or p.y > maxp.y or p.y < minp.y or p.z > maxp.z or p.z < minp.z then
  94. table.remove(positions, i)
  95. goto redo
  96. end
  97. end
  98. end
  99. end
  100. -- Return only positions that matter.
  101. return positions
  102. end
  103. minetest.register_privilege("delprotect", {
  104. description = "Ignore player protection.",
  105. give_to_singleplayer = false,
  106. })
  107. protector.get_member_list = function(meta)
  108. return meta:get_string("members"):split(" ")
  109. end
  110. protector.set_member_list = function(meta, list)
  111. meta:set_string("members", table.concat(list, " "))
  112. end
  113. protector.is_member = function(meta, name)
  114. for _, n in pairs(protector.get_member_list(meta)) do
  115. if n == name then
  116. return true
  117. end
  118. end
  119. return false
  120. end
  121. protector.add_member = function(meta, name)
  122. -- Constant (20) defined by player.h
  123. if name:len() > 25 then
  124. return
  125. end
  126. name = rename.grn(name)
  127. if protector.is_member(meta, name) then
  128. return
  129. end
  130. local list = protector.get_member_list(meta)
  131. if #list >= protector.max_share_count then
  132. return
  133. end
  134. table.insert(list, name)
  135. protector.set_member_list(meta, list)
  136. end
  137. protector.del_member = function(meta, name)
  138. name = rename.grn(name)
  139. local list = protector.get_member_list(meta)
  140. for i, n in pairs(list) do
  141. if n == name then
  142. table.remove(list, i)
  143. break
  144. end
  145. end
  146. protector.set_member_list(meta, list)
  147. end
  148. -- Protector Interface
  149. protector.generate_formspec = function(meta)
  150. local formspec = "size[8,7]"
  151. .. default.formspec.get_form_colors()
  152. .. default.formspec.get_form_image()
  153. .. default.formspec.get_slot_colors()
  154. .. "label[0,0;" .. "11x11x11 Protector Interface" .. "]"
  155. .. "label[0,0.5;" .. "PUNCH node to show protected area or USE for area check." .. "]"
  156. .. "label[0,2;" .. "Members:" .. "]"
  157. .. "button_exit[3,6.2;2,0.5;close_me;" .. "Close" .. "]"
  158. .. "field_close_on_enter[protector_add_member;false]"
  159. local members = protector.get_member_list(meta)
  160. local npp = protector.max_share_count -- max users added onto protector list
  161. local i = 0
  162. for n = 1, #members do
  163. if i < npp then
  164. -- show username
  165. formspec = formspec .. "button[" .. (i % 4 * 2)
  166. .. "," .. math.floor(i / 4 + 3)
  167. .. ";1.5,.5;protector_member;" .. rename.gpn(members[n]) .. "]"
  168. -- username remove button
  169. .. "button[" .. (i % 4 * 2 + 1.25) .. ","
  170. .. math.floor(i / 4 + 3)
  171. .. ";.75,.5;protector_del_member_" .. members[n] .. ";X]"
  172. end
  173. i = i + 1
  174. end
  175. if i < npp then
  176. -- user name entry field
  177. formspec = formspec .. "field[" .. (i % 4 * 2 + 1 / 3) .. ","
  178. .. (math.floor(i / 4 + 3) + 1 / 3)
  179. .. ";1.433,.5;protector_add_member;;]"
  180. -- username add button
  181. .."button[" .. (i % 4 * 2 + 1.25) .. ","
  182. .. math.floor(i / 4 + 3) .. ";.75,.5;protector_submit;+]"
  183. end
  184. return formspec
  185. end
  186. protector.get_node_owner = function(pos)
  187. local r = protector.radius
  188. local positions = protector.find_protector_nodes(pos, r, 1, "")
  189. for n = 1, #positions do
  190. local meta = minetest.get_meta(positions[n])
  191. local owner = meta:get_string("owner")
  192. return owner
  193. end
  194. end
  195. -- This is called by the node inspector tool to toggle display entities for buried protectors.
  196. protector.toggle_protector_entities_in_area = function(pname, pos)
  197. local r = protector.radius
  198. local positions = protector.find_protector_nodes(pos, r, 1, "")
  199. for n = 1, #positions do
  200. local meta = minetest.get_meta(positions[n])
  201. local owner = meta:get_string("owner")
  202. if owner == pname then -- Can only toggle display entities for owned protectors.
  203. local node = minetest.get_node(positions[n])
  204. if node.name == "protector:protect" or node.name == "protector:protect2" then
  205. protector.toggle_area_display(positions[n], "protector:display")
  206. elseif node.name == "protector:protect3" or node.name == "protector:protect4" then
  207. protector.toggle_area_display(positions[n], "protector:display_small")
  208. end
  209. end
  210. end
  211. end
  212. -- Infolevel:
  213. -- 0 for no info
  214. -- 1 for "This area is owned by <owner> !" if you can't dig
  215. -- 2 for "This area is owned by <owner>.
  216. -- 3 for checking protector overlaps
  217. protector.can_dig = function(r, mult, nodename, pos, digger, onlyowner, infolevel)
  218. if not digger or not pos then
  219. return false
  220. end
  221. -- Bedrock is always protected.
  222. local node = minetest.get_node(pos)
  223. local ndef = minetest.reg_ns_nodes[node.name] or
  224. minetest.registered_nodes[node.name]
  225. if not ndef then
  226. return false
  227. end
  228. if ndef.always_protected then
  229. return false
  230. end
  231. -- Delprotect privileged users can override protections.
  232. if (minetest.check_player_privs(digger, {delprotect = true}) or minetest.check_player_privs(digger, {protection_bypass = true}))
  233. and
  234. (infolevel == 1 or infolevel == 0) then
  235. return true
  236. end
  237. -- Prevent users from modifying the map outside of any realm.
  238. if not rc.is_valid_realm_pos(pos) then
  239. return false
  240. end
  241. if infolevel == 3 then
  242. infolevel = 1
  243. end
  244. -- Find the protector nodes.
  245. local positions = protector.find_protector_nodes(pos, r, mult, nodename)
  246. local meta, owner, members
  247. -- Anyone can dig nodes marked as override in their meta.
  248. -- We don't bother checking the meta unless a protector is present.
  249. if #positions > 0 then
  250. -- `protection_cancel' only applies if the node is NOT buildable_to.
  251. -- This ensures players cannot break protection by dropping `buildable_to' nodes, and then placing into them.
  252. -- The falling block code is responsible for ensuring that a `buildable_to'
  253. -- node can never be dropped into a protected zone.
  254. local def = minetest.reg_ns_nodes[node.name] or
  255. minetest.registered_nodes[node.name]
  256. if def and not def.buildable_to then
  257. local meta2 = minetest.get_meta(pos)
  258. -- This is generally only set on nodes which have fallen.
  259. if meta2:get_int("protection_cancel") == 1 then
  260. -- Pretend that we found no protectors.
  261. -- Continue code as if we found none.
  262. positions = {}
  263. end
  264. end
  265. end
  266. for n = 1, #positions do
  267. meta = minetest.get_meta(positions[n])
  268. owner = meta:get_string("owner") or ""
  269. members = meta:get_string("members") or ""
  270. if owner ~= digger then
  271. if onlyowner or not protector.is_member(meta, digger) then
  272. if infolevel == 1 then
  273. minetest.chat_send_player(digger, "# Server: This area is protected by <" .. rename.gpn(owner) .. ">!")
  274. elseif infolevel == 2 then
  275. minetest.chat_send_player(digger, "# Server: This area is protected by <" .. rename.gpn(owner) .. ">.")
  276. minetest.chat_send_player(digger, "# Server: Protection located at: " .. rc.pos_to_namestr(positions[n]))
  277. if members ~= "" then
  278. minetest.chat_send_player(digger, "# Server: Members: [" .. members .. "].")
  279. end
  280. end
  281. return false
  282. end
  283. end
  284. if infolevel == 2 then
  285. minetest.chat_send_player(digger, "# Server: This area is protected by <" .. rename.gpn(owner) .. ">.")
  286. minetest.chat_send_player(digger, "# Server: Protection located at: " .. rc.pos_to_namestr(positions[n]))
  287. if members ~= "" then
  288. minetest.chat_send_player(digger, "# Server: Members: [" .. members .. "].")
  289. end
  290. return false
  291. end
  292. end
  293. if infolevel == 2 then
  294. if #positions < 1 then
  295. minetest.chat_send_player(digger, "# Server: This area is not protected.")
  296. end
  297. minetest.chat_send_player(digger, "# Server: You can build here.")
  298. end
  299. return true
  300. end
  301. -- Can node be added or removed, if so return node else true (for protected)
  302. function protector.punish_player(pos, pname)
  303. if not pname or pname == "" then
  304. return
  305. end
  306. local player = minetest.get_player_by_name(pname)
  307. if not player or not player:is_player() then
  308. return
  309. end
  310. -- hurt player if protection violated
  311. if protector.hurt > 0 then
  312. local hp = player:get_hp()
  313. if hp > 0 then -- Avoid writing message twice.
  314. player:set_hp(hp - protector.hurt)
  315. if player:get_hp() <= 0 then
  316. minetest.chat_send_all("# Server: <" .. rename.gpn(pname) .. "> was killed by a protection block.")
  317. end
  318. end
  319. end
  320. -- flip player when protection violated
  321. if protector.flip then
  322. -- yaw +/- 180°
  323. local yaw = player:get_look_horizontal() + math.random(-math.pi, math.pi)
  324. player:set_look_horizontal(yaw)
  325. -- Invert pitch.
  326. player:set_look_vertical(-player:get_look_vertical())
  327. -- if digging below player, move up to avoid falling through hole
  328. local pla_pos = player:get_pos()
  329. if pos.y < pla_pos.y then
  330. player:set_pos({
  331. x = pla_pos.x,
  332. y = pla_pos.y + 0.8,
  333. z = pla_pos.z
  334. })
  335. end
  336. end
  337. -- drop tool/item if protection violated
  338. -- This is disabled because it is too easy to exploit using lag -- TenPlus1.
  339. --[[
  340. if protector.drop == true then
  341. local holding = player:get_wielded_item()
  342. if holding:to_string() ~= "" then
  343. -- take stack
  344. local sta = holding:take_item(holding:get_count())
  345. player:set_wielded_item(holding)
  346. -- incase of lag, reset stack
  347. minetest.after(0.1, function()
  348. -- Get player reference anew, in case player has left game.
  349. local player = minetest.get_player_by_name(pname)
  350. if not player then
  351. return
  352. end
  353. player:set_wielded_item(holding)
  354. -- drop stack
  355. local obj = minetest.add_item(player:get_pos(), sta)
  356. if obj then
  357. obj:setvelocity({x = math.random(-5, 5), y = 5, z = math.random(-5, 5)})
  358. end
  359. end)
  360. end
  361. end
  362. --]]
  363. end
  364. protector.old_is_protected = minetest.is_protected
  365. function minetest.is_protected(pos, digger, nodename)
  366. digger = digger or "" -- nil check
  367. -- Allow protection to be temporarily disabled for API purposes.
  368. if not PROTECTION_ENABLED then
  369. return protector.old_is_protected(pos, digger)
  370. end
  371. if not protector.can_dig(protector.radius, 1, "", pos, digger, false, 1) then
  372. -- Slight delay to separate call stacks; hopefully this fixes the rare recursion/crash issue.
  373. -- Update: it seems to have fixed the rare crashes, there have been no more related to this since some months.
  374. minetest.after(0, function()
  375. protector.punish_player(pos, digger)
  376. end)
  377. return true
  378. end
  379. return protector.old_is_protected(pos, digger)
  380. end
  381. -- Called when protection should be checked, but no retaliation should be carried out.
  382. function minetest.test_protection(pos, digger)
  383. digger = digger or "" -- nil check
  384. -- Allow protection to be temporarily disabled for API purposes.
  385. if not PROTECTION_ENABLED then
  386. return protector.old_is_protected(pos, digger)
  387. end
  388. -- Don't give chat messages. Infolevel 0.
  389. if not protector.can_dig(protector.radius, 1, "", pos, digger, false, 0) then
  390. return true
  391. end
  392. return protector.old_is_protected(pos, digger)
  393. end
  394. -- Make sure protection block doesn't overlap another protector's area
  395. function protector.check_overlap_main(protname, pname, spos)
  396. if not realm_allows_protection(spos, pname) then
  397. return false, 4
  398. end
  399. -- Do not allow protections to be placed near the Outback gate's exit coordinates.
  400. -- Note: this prevents the admin from placing protection here, too.
  401. -- Existing protections (if any) remain untouched.
  402. if not serveressentials.protector_can_place(spos) then
  403. return false, 5
  404. end
  405. if not protector.can_dig(protector.radius, 2, protname, spos, pname, true, 3) then
  406. -- Overlap with other player's protection.
  407. return false, 1
  408. end
  409. local pos = {x=spos.x, y=spos.y, z=spos.z}
  410. local rad = protector.radius
  411. local bones = minetest.find_nodes_in_area(
  412. {x = pos.x - rad, y = pos.y - rad, z = pos.z - rad},
  413. {x = pos.x + rad, y = pos.y + rad, z = pos.z + rad},
  414. {"bones:bones"})
  415. if bones and #bones > 0 then
  416. for k, v in ipairs(bones) do
  417. local meta = minetest.get_meta(v)
  418. local owner = meta:get_string("owner") or ""
  419. if owner ~= "" and owner ~= "server" then
  420. -- fresh bones nearby
  421. return false, 2
  422. end
  423. local oldowner = meta:get_string("oldowner") or ""
  424. if oldowner ~= "" and oldowner ~= "server" then
  425. -- old bones nearby
  426. return false, 3
  427. end
  428. end
  429. end
  430. -- no overlap with other protection
  431. return true, 0
  432. end
  433. function protector.check_overlap(itemstack, placer, pt)
  434. if pt.type ~= "node" then
  435. return itemstack
  436. end
  437. local pname = placer:get_player_name()
  438. local prot_type = itemstack:get_name()
  439. local success, reason = protector.check_overlap_main(prot_type, pname, pt.above)
  440. if not success then
  441. if reason == 1 then
  442. minetest.chat_send_player(pname, "# Server: Protection bounds overlap into another person's area claim.")
  443. elseif reason == 2 then
  444. minetest.chat_send_player(pname, "# Server: You cannot claim this area while someone's fresh corpse is nearby!")
  445. elseif reason == 3 then
  446. minetest.chat_send_player(pname, "# Server: You must remove all corpses before you can claim this area.")
  447. elseif reason == 4 then
  448. minetest.chat_send_player(pname, "# Server: Cannot claim protection here, there is no land authority.")
  449. elseif reason == 5 then
  450. minetest.chat_send_player(pname, "# Server: The area near the Outback gate's exit is public. Cannot claim land here.")
  451. else
  452. minetest.chat_send_player(pname, "# Server: Cannot place protection for unknown reason.")
  453. end
  454. return
  455. end
  456. return minetest.item_place(itemstack, placer, pt)
  457. end
  458. -- Called in the `after_place_node` of protector:protect3 and protector:protect4.
  459. function protector.timed_setup(pos, placer, meta)
  460. local pname = placer:get_player_name()
  461. -- If protector was placed by someone without a Key, then it is a temporary protector.
  462. -- Set up timer stuff if needed.
  463. if not passport.player_has_key(pname, placer) then
  464. local timer = minetest.get_node_timer(pos)
  465. timer:start(60) -- Run once a minute.
  466. local hours = 720 -- 1 month, or 30 days.
  467. local minutes = hours * 60
  468. meta:set_int("temprot", 1)
  469. meta:set_int("timerot", minutes)
  470. meta:mark_as_private({"temprot", "timerot"})
  471. end
  472. end
  473. -- Called in the `on_timer` of protector:protect3 and protector:protect4.
  474. function protector.on_timer(pos, elapsed)
  475. local meta = minetest.get_meta(pos)
  476. if meta:get_int("temprot") == 1 then
  477. local minutes = meta:get_int("timerot")
  478. minutes = minutes - math.floor(elapsed / 60)
  479. if minutes >= 0 then
  480. meta:set_int("timerot", minutes)
  481. meta:set_string("infotext", protector.get_infotext(meta))
  482. -- Rerun timer for same timeout.
  483. minetest.get_node_timer(pos):start(60)
  484. return
  485. end
  486. -- Replace protector with expired protector node.
  487. -- Preserve param2 and node appearance.
  488. local node = minetest.get_node(pos)
  489. local ndef = minetest.registered_nodes[node.name]
  490. node.name = ndef._expired_protector_name
  491. minetest.set_node(pos, node)
  492. end
  493. end
  494. -- Using data from the meta, assemble and return an infotext string.
  495. function protector.get_infotext(meta)
  496. local owner = meta:get_string("owner")
  497. local dname = rename.gpn(owner)
  498. local pdate = meta:get_string("placedate")
  499. if not pdate or pdate == "" then
  500. pdate = "an unknown date!"
  501. end
  502. local timeout = ""
  503. if meta:get_int("temprot") == 1 then
  504. local minutes = meta:get_int("timerot")
  505. if minutes < 0 then minutes = 0 end
  506. local hours = math.floor(minutes / 60)
  507. timeout = "\n------------------------------------------\nExpires in " .. hours .. " hours\nGet KEY to make permanent claims"
  508. end
  509. return "Protection (Owned by <" .. dname .. ">!)\nPlaced on " .. pdate .. timeout
  510. end
  511. --= Protection Lock
  512. minetest.register_node("protector:protect", {
  513. description = "Advanced Protection Lock\nArea Protected: 11x11x11",
  514. drawtype = "nodebox",
  515. tiles = {
  516. "moreblocks_circle_stone_bricks.png",
  517. "moreblocks_circle_stone_bricks.png",
  518. "moreblocks_circle_stone_bricks.png^protector_logo.png"
  519. },
  520. sounds = default.node_sound_stone_defaults(),
  521. groups = utility.dig_groups("bigitem", {
  522. immovable = 1, -- No pistons, no nothing.
  523. protector = 1,
  524. }),
  525. is_ground_content = false,
  526. paramtype = "light",
  527. movement_speed_multiplier = default.NORM_SPEED,
  528. -- Protectors shall not emit light. By MustTest
  529. --light_source = 4,
  530. node_box = {
  531. type = "fixed",
  532. fixed = {
  533. {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
  534. }
  535. },
  536. on_place = protector.check_overlap,
  537. after_place_node = function(pos, placer)
  538. local meta = minetest.get_meta(pos)
  539. local placedate = get_public_time()
  540. local pname = placer:get_player_name() or ""
  541. local dname = rename.gpn(pname)
  542. meta:set_string("placedate", placedate)
  543. meta:set_string("owner", pname)
  544. meta:set_string("rename", dname)
  545. meta:set_string("infotext", protector.get_infotext(meta))
  546. meta:set_string("members", "")
  547. -- Notify nearby players.
  548. protector.update_nearby_players(pos)
  549. end,
  550. on_use = function(itemstack, user, pointed_thing)
  551. if pointed_thing.type ~= "node" then
  552. return
  553. end
  554. protector.can_dig(protector.radius, 1, "protector:protect", pointed_thing.under, user:get_player_name(), false, 2)
  555. end,
  556. on_rightclick = function(pos, node, clicker, itemstack)
  557. local meta = minetest.get_meta(pos)
  558. local name = clicker:get_player_name() or ""
  559. if meta and protector.can_dig(1, 1, "protector:protect", pos, name, true, 1) then
  560. player_pos[name] = pos
  561. minetest.show_formspec(name, "protector:node", protector.generate_formspec(meta))
  562. end
  563. end,
  564. on_punch = function(pos, node, puncher)
  565. if minetest.test_protection(pos, puncher:get_player_name()) then
  566. return
  567. end
  568. protector.toggle_area_display(pos, "protector:display")
  569. end,
  570. can_dig = function(pos, player)
  571. return player and protector.can_dig(1, 1, "protector:protect", pos, player:get_player_name(), true, 1)
  572. end,
  573. -- TNT-proof.
  574. on_blast = function() end,
  575. -- Called by rename LBM.
  576. _on_rename_check = function(pos)
  577. local meta = minetest.get_meta(pos)
  578. local owner = meta:get_string("owner")
  579. -- Nobody placed this block.
  580. if owner == "" then
  581. return
  582. end
  583. local cname = meta:get_string("rename")
  584. local dname = rename.gpn(owner)
  585. meta:set_string("rename", dname)
  586. meta:set_string("infotext", protector.get_infotext(meta))
  587. end,
  588. on_destruct = function(pos)
  589. -- Notify nearby players.
  590. minetest.after(0, protector.update_nearby_players, pos)
  591. return protector.remove_area_display(pos)
  592. end,
  593. })
  594. minetest.register_node("protector:protect3", {
  595. description = "Protection Lock\nArea Protected: 7x7x7",
  596. drawtype = "nodebox",
  597. tiles = {"cityblock.png"},
  598. sounds = default.node_sound_stone_defaults(),
  599. groups = utility.dig_groups("bigitem", {
  600. immovable = 1, -- No pistons, no nothing.
  601. protector = 1,
  602. }),
  603. is_ground_content = false,
  604. paramtype = "light",
  605. movement_speed_multiplier = default.NORM_SPEED,
  606. -- Protectors shall not emit light. By MustTest
  607. --light_source = 4,
  608. node_box = {
  609. type = "fixed",
  610. fixed = {
  611. {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
  612. }
  613. },
  614. on_place = protector.check_overlap,
  615. after_place_node = function(pos, placer)
  616. local meta = minetest.get_meta(pos)
  617. protector.timed_setup(pos, placer, meta)
  618. local placedate = get_public_time()
  619. local pname = placer:get_player_name() or ""
  620. local dname = rename.gpn(pname)
  621. meta:set_string("placedate", placedate)
  622. meta:set_string("owner", pname)
  623. meta:set_string("rename", dname)
  624. meta:set_string("infotext", protector.get_infotext(meta))
  625. meta:set_string("members", "")
  626. -- Notify nearby players.
  627. protector.update_nearby_players(pos)
  628. end,
  629. _expired_protector_name = "protector:expired1",
  630. on_timer = protector.on_timer,
  631. on_use = function(itemstack, user, pointed_thing)
  632. if pointed_thing.type ~= "node" then
  633. return
  634. end
  635. protector.can_dig(protector.radius, 1, "protector:protect3", pointed_thing.under, user:get_player_name(), false, 2)
  636. end,
  637. -- This protector does not have a formspec, no on_rightclick defined.
  638. on_punch = function(pos, node, puncher)
  639. if minetest.test_protection(pos, puncher:get_player_name()) then
  640. return
  641. end
  642. protector.toggle_area_display(pos, "protector:display_small")
  643. end,
  644. can_dig = function(pos, player)
  645. return player and protector.can_dig(1, 1, "protector:protect3", pos, player:get_player_name(), true, 1)
  646. end,
  647. -- TNT-proof.
  648. on_blast = function() end,
  649. -- Called by rename LBM.
  650. _on_rename_check = function(pos)
  651. local meta = minetest.get_meta(pos)
  652. local owner = meta:get_string("owner")
  653. local placedate = meta:get_string("placedate")
  654. -- Nobody placed this block.
  655. if owner == "" then
  656. return
  657. end
  658. if placedate == "" then
  659. placedate = "an unknown date!"
  660. end
  661. local cname = meta:get_string("rename")
  662. local dname = rename.gpn(owner)
  663. meta:set_string("rename", dname)
  664. meta:set_string("infotext", protector.get_infotext(meta))
  665. end,
  666. on_destruct = function(pos)
  667. -- Notify nearby players.
  668. minetest.after(0, protector.update_nearby_players, pos)
  669. return protector.remove_area_display(pos)
  670. end,
  671. })
  672. --= Protection Logo
  673. minetest.register_node("protector:protect2", {
  674. description = "Advanced Protection Logo\nArea Protected: 11x11x11",
  675. tiles = {"protector_logo.png"},
  676. wield_image = "protector_logo.png",
  677. inventory_image = "protector_logo.png",
  678. sounds = default.node_sound_stone_defaults(),
  679. groups = utility.dig_groups("bigitem", {
  680. immovable = 1, -- No pistons, no nothing.
  681. protector = 1,
  682. }),
  683. paramtype = 'light',
  684. paramtype2 = "wallmounted",
  685. legacy_wallmounted = true,
  686. -- Protectors shall not emit light. By MustTest
  687. --light_source = 4,
  688. drawtype = "nodebox",
  689. sunlight_propagates = true,
  690. walkable = false,
  691. node_box = {
  692. type = "wallmounted",
  693. wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
  694. wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
  695. wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
  696. },
  697. selection_box = {type = "wallmounted"},
  698. on_place = protector.check_overlap,
  699. after_place_node = function(pos, placer)
  700. local meta = minetest.get_meta(pos)
  701. local placedate = get_public_time()
  702. local pname = placer:get_player_name() or ""
  703. local dname = rename.gpn(pname)
  704. meta:set_string("placedate", placedate)
  705. meta:set_string("owner", pname)
  706. meta:set_string("rename", dname)
  707. meta:set_string("infotext", protector.get_infotext(meta))
  708. meta:set_string("members", "")
  709. -- Notify nearby players.
  710. protector.update_nearby_players(pos)
  711. end,
  712. on_use = function(itemstack, user, pointed_thing)
  713. if pointed_thing.type ~= "node" then
  714. return
  715. end
  716. protector.can_dig(protector.radius, 1, "protector:protect2", pointed_thing.under, user:get_player_name(), false, 2)
  717. end,
  718. on_rightclick = function(pos, node, clicker, itemstack)
  719. local meta = minetest.get_meta(pos)
  720. local name = clicker:get_player_name() or ""
  721. if meta and protector.can_dig(1, 1, "protector:protect2", pos, name, true, 1) then
  722. player_pos[name] = pos
  723. minetest.show_formspec(name, "protector:node", protector.generate_formspec(meta))
  724. end
  725. end,
  726. on_punch = function(pos, node, puncher)
  727. if minetest.test_protection(pos, puncher:get_player_name()) then
  728. return
  729. end
  730. protector.toggle_area_display(pos, "protector:display")
  731. end,
  732. can_dig = function(pos, player)
  733. return player and protector.can_dig(1, 1, "protector:protect2", pos, player:get_player_name(), true, 1)
  734. end,
  735. -- TNT-proof.
  736. on_blast = function() end,
  737. -- Called by rename LBM.
  738. _on_rename_check = function(pos)
  739. local meta = minetest.get_meta(pos)
  740. local owner = meta:get_string("owner")
  741. local placedate = meta:get_string("placedate")
  742. -- Nobody placed this block.
  743. if owner == "" then
  744. return
  745. end
  746. if placedate == "" then
  747. placedate = "an unknown date!"
  748. end
  749. local cname = meta:get_string("rename")
  750. local dname = rename.gpn(owner)
  751. meta:set_string("rename", dname)
  752. meta:set_string("infotext", protector.get_infotext(meta))
  753. end,
  754. on_destruct = function(pos)
  755. -- Notify nearby players.
  756. minetest.after(0, protector.update_nearby_players, pos)
  757. return protector.remove_area_display(pos)
  758. end,
  759. })
  760. minetest.register_node("protector:protect4", {
  761. description = "Protection Logo\nArea Protected: 7x7x7",
  762. tiles = {"protector_lock.png"},
  763. wield_image = "protector_lock.png",
  764. inventory_image = "protector_lock.png",
  765. sounds = default.node_sound_stone_defaults(),
  766. groups = utility.dig_groups("bigitem", {
  767. immovable = 1, -- No pistons, no nothing.
  768. protector = 1,
  769. }),
  770. paramtype = 'light',
  771. paramtype2 = "wallmounted",
  772. legacy_wallmounted = true,
  773. -- Protectors shall not emit light. By MustTest
  774. --light_source = 4,
  775. drawtype = "nodebox",
  776. sunlight_propagates = true,
  777. walkable = false,
  778. node_box = {
  779. type = "wallmounted",
  780. wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
  781. wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
  782. wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
  783. },
  784. selection_box = {type = "wallmounted"},
  785. on_place = protector.check_overlap,
  786. after_place_node = function(pos, placer)
  787. local meta = minetest.get_meta(pos)
  788. protector.timed_setup(pos, placer, meta)
  789. local placedate = get_public_time()
  790. local pname = placer:get_player_name() or ""
  791. local dname = rename.gpn(pname)
  792. meta:set_string("placedate", placedate)
  793. meta:set_string("owner", pname)
  794. meta:set_string("rename", dname)
  795. meta:set_string("infotext", protector.get_infotext(meta))
  796. meta:set_string("members", "")
  797. -- Notify nearby players.
  798. protector.update_nearby_players(pos)
  799. end,
  800. _expired_protector_name = "protector:expired2",
  801. on_timer = protector.on_timer,
  802. on_use = function(itemstack, user, pointed_thing)
  803. if pointed_thing.type ~= "node" then
  804. return
  805. end
  806. protector.can_dig(protector.radius, 1, "protector:protect4", pointed_thing.under, user:get_player_name(), false, 2)
  807. end,
  808. -- This protector does not have a formspec, no on_rightclick defined.
  809. on_punch = function(pos, node, puncher)
  810. if minetest.test_protection(pos, puncher:get_player_name()) then
  811. return
  812. end
  813. protector.toggle_area_display(pos, "protector:display_small")
  814. end,
  815. can_dig = function(pos, player)
  816. return player and protector.can_dig(1, 1, "protector:protect4", pos, player:get_player_name(), true, 1)
  817. end,
  818. -- TNT-proof.
  819. on_blast = function() end,
  820. -- Called by rename LBM.
  821. _on_rename_check = function(pos)
  822. local meta = minetest.get_meta(pos)
  823. local owner = meta:get_string("owner")
  824. local placedate = meta:get_string("placedate")
  825. -- Nobody placed this block.
  826. if owner == "" then
  827. return
  828. end
  829. if placedate == "" then
  830. placedate = "an unknown date!"
  831. end
  832. local cname = meta:get_string("rename")
  833. local dname = rename.gpn(owner)
  834. meta:set_string("rename", dname)
  835. meta:set_string("infotext", protector.get_infotext(meta))
  836. end,
  837. on_destruct = function(pos)
  838. -- Notify nearby players.
  839. minetest.after(0, protector.update_nearby_players, pos)
  840. return protector.remove_area_display(pos)
  841. end,
  842. })
  843. -- If name entered or button press
  844. minetest.register_on_player_receive_fields(function(player, formname, fields)
  845. if formname ~= "protector:node" then
  846. return
  847. end
  848. local pname = player:get_player_name() or "" -- Nil check.
  849. local pos = player_pos[pname] -- Context should have been created during on_rightclick. CSM protection.
  850. -- Localize field member.
  851. local add_member_input = fields.protector_add_member
  852. -- Reset formspec until close button pressed.
  853. if (fields.close_me or fields.quit)
  854. and (not add_member_input or add_member_input == "") then
  855. player_pos[pname] = nil
  856. return true
  857. end
  858. if not pos then
  859. return true
  860. end
  861. local meta = minetest.get_meta(pos)
  862. local node = minetest.get_node(pos)
  863. -- Meta nil check.
  864. if not meta then
  865. return true
  866. end
  867. -- Are we actually working on a protection node? (CSM protection.)
  868. if node.name ~= "protector:protect"
  869. and node.name ~= "protector:protect2"
  870. and node.name ~= "protector:protect3"
  871. and node.name ~= "protector:protect4" then
  872. player_pos[pname] = nil
  873. return true
  874. end
  875. -- Only advanced protectors support member names.
  876. if node.name == "protector:protect3" or node.name == "protector:protect4" then
  877. minetest.chat_send_player(pname, "# Server: Sharing feature not supported by basic protectors!")
  878. return true
  879. end
  880. -- Do not permit caller to modify a protector they do not own.
  881. if not protector.can_dig(1, 1, node.name, pos, pname, true, 1) then
  882. return true
  883. end
  884. if add_member_input then
  885. for _, i in pairs(add_member_input:split(" ")) do
  886. protector.add_member(meta, i)
  887. end
  888. end
  889. for field, value in pairs(fields) do
  890. if string.sub(field, 0, string.len("protector_del_member_")) == "protector_del_member_" then
  891. protector.del_member(meta, string.sub(field,string.len("protector_del_member_") + 1))
  892. end
  893. end
  894. -- Clear formspec context.
  895. minetest.show_formspec(pname, formname, protector.generate_formspec(meta))
  896. return true
  897. end)
  898. -- Display entity shown when protector node is punched
  899. minetest.register_entity("protector:display", {
  900. physical = false,
  901. collisionbox = {0, 0, 0, 0, 0, 0},
  902. visual = "wielditem",
  903. -- wielditem seems to be scaled to 1.5 times original node size
  904. visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
  905. textures = {"protector:display_node"},
  906. timer = 0,
  907. glow = 14,
  908. on_step = function(self, dtime)
  909. self.timer = self.timer + dtime
  910. if self.timer > protector.display_time then
  911. self.object:remove()
  912. end
  913. end,
  914. on_blast = function(self, damage)
  915. return false, false, {}
  916. end,
  917. })
  918. minetest.register_entity("protector:display_small", {
  919. physical = false,
  920. collisionbox = {0, 0, 0, 0, 0, 0},
  921. visual = "wielditem",
  922. -- wielditem seems to be scaled to 1.5 times original node size
  923. visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
  924. textures = {"protector:display_node_small"},
  925. timer = 0,
  926. glow = 14,
  927. on_step = function(self, dtime)
  928. self.timer = self.timer + dtime
  929. if self.timer > protector.display_time then
  930. self.object:remove()
  931. end
  932. end,
  933. on_blast = function(self, damage)
  934. return false, false, {}
  935. end,
  936. })
  937. -- Display-zone node, Do NOT place the display as a node,
  938. -- it is made to be used as an entity (see above)
  939. do
  940. local x = protector.radius
  941. minetest.register_node("protector:display_node", {
  942. tiles = {"protector_display.png"},
  943. use_texture_alpha = true,
  944. walkable = false,
  945. drawtype = "nodebox",
  946. node_box = {
  947. type = "fixed",
  948. fixed = {
  949. -- sides
  950. {-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
  951. {-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
  952. {(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
  953. {-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
  954. -- top
  955. {-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
  956. -- bottom
  957. {-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
  958. -- middle (surround protector)
  959. {-.55,-.55,-.55, .55,.55,.55},
  960. },
  961. },
  962. selection_box = {
  963. type = "regular",
  964. },
  965. paramtype = "light",
  966. groups = utility.dig_groups("item", {not_in_creative_inventory = 1}),
  967. drop = "",
  968. })
  969. end
  970. do
  971. local x = protector.radius_small
  972. minetest.register_node("protector:display_node_small", {
  973. tiles = {"protector_display.png"},
  974. use_texture_alpha = true,
  975. walkable = false,
  976. drawtype = "nodebox",
  977. node_box = {
  978. type = "fixed",
  979. fixed = {
  980. -- sides
  981. {-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
  982. {-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
  983. {(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
  984. {-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
  985. -- top
  986. {-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
  987. -- bottom
  988. {-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
  989. -- middle (surround protector)
  990. {-.55,-.55,-.55, .55,.55,.55},
  991. },
  992. },
  993. selection_box = {
  994. type = "regular",
  995. },
  996. paramtype = "light",
  997. groups = utility.dig_groups("item", {not_in_creative_inventory = 1}),
  998. drop = "",
  999. })
  1000. end
  1001. function protector.remove_area_display(pos)
  1002. local ents = minetest.get_objects_inside_radius(pos, 0.5)
  1003. for k, n in ipairs(ents) do
  1004. if not n:is_player() and n:get_luaentity() then
  1005. local name = n:get_luaentity().name or ""
  1006. if name == "protector:display" or name == "protector:display_small" then
  1007. n:remove()
  1008. end
  1009. end
  1010. end
  1011. end
  1012. function protector.toggle_area_display(pos, entity)
  1013. local got_any = false
  1014. local ents = minetest.get_objects_inside_radius(pos, 0.5)
  1015. for k, n in ipairs(ents) do
  1016. if not n:is_player() and n:get_luaentity() then
  1017. local name = n:get_luaentity().name or ""
  1018. if name == "protector:display" or name == "protector:display_small" then
  1019. n:remove()
  1020. got_any = true
  1021. end
  1022. end
  1023. end
  1024. if not got_any then
  1025. minetest.add_entity(pos, entity)
  1026. end
  1027. end
  1028. dofile(protector.modpath .. "/crafts.lua")
  1029. -- Expired protector node.
  1030. minetest.register_node("protector:expired1", {
  1031. description = "Expired Protector",
  1032. drawtype = "nodebox",
  1033. tiles = {"cityblock.png"},
  1034. sounds = default.node_sound_stone_defaults(),
  1035. groups = utility.dig_groups("bigitem"),
  1036. paramtype = "light",
  1037. movement_speed_multiplier = default.NORM_SPEED,
  1038. node_box = {
  1039. type = "fixed",
  1040. fixed = {
  1041. {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
  1042. }
  1043. },
  1044. })
  1045. minetest.register_node("protector:expired2", {
  1046. description = "Expired Protector",
  1047. tiles = {"protector_lock.png"},
  1048. wield_image = "protector_lock.png",
  1049. inventory_image = "protector_lock.png",
  1050. sounds = default.node_sound_stone_defaults(),
  1051. groups = utility.dig_groups("bigitem"),
  1052. paramtype = 'light',
  1053. paramtype2 = "wallmounted",
  1054. legacy_wallmounted = true,
  1055. drawtype = "nodebox",
  1056. sunlight_propagates = true,
  1057. walkable = false,
  1058. node_box = {
  1059. type = "wallmounted",
  1060. wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
  1061. wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
  1062. wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
  1063. },
  1064. selection_box = {type = "wallmounted"},
  1065. })