init.lua 36 KB

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