functions.lua 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. -- Localize vector.distance() for performance.
  2. local vector_distance = vector.distance
  3. local math_random = math.random
  4. -- This function is responsible for triggering the update of vending
  5. -- machines when a chest inventory is modified.
  6. chest_api.update_vending = function(pos)
  7. local vendors = {}
  8. local node = nil
  9. local valid_vendor = function(name)
  10. local names = {
  11. "easyvend:vendor",
  12. "easyvend:vendor_on",
  13. "easyvend:depositor",
  14. "easyvend:depositor_on",
  15. }
  16. for k, v in ipairs(names) do
  17. if v == name then
  18. return true
  19. end
  20. end
  21. end
  22. local orig_pos = table.copy(pos)
  23. -- Search upwards.
  24. pos.y = pos.y + 1
  25. node = minetest.get_node_or_nil(pos)
  26. while node ~= nil do
  27. if easyvend.traversable_node_types[node.name] then
  28. if valid_vendor(node.name) then
  29. vendors[#vendors+1] = {pos=table.copy(pos), node=table.copy(node)}
  30. end
  31. else
  32. break
  33. end
  34. pos.y = pos.y + 1
  35. node = minetest.get_node_or_nil(pos)
  36. end
  37. pos = table.copy(orig_pos)
  38. -- Search downwards.
  39. pos.y = pos.y - 1
  40. node = minetest.get_node_or_nil(pos)
  41. while node ~= nil do
  42. if easyvend.traversable_node_types[node.name] then
  43. if valid_vendor(node.name) then
  44. vendors[#vendors+1] = {pos=table.copy(pos), node=table.copy(node)}
  45. end
  46. else
  47. break
  48. end
  49. pos.y = pos.y - 1
  50. node = minetest.get_node_or_nil(pos)
  51. end
  52. -- Perform vending checks!
  53. for k, v in ipairs(vendors) do
  54. easyvend.machine_check(v.pos, v.node)
  55. --minetest.chat_send_all("# Server: Machine check!")
  56. end
  57. end
  58. function chest_api.sort_chest(pos)
  59. local meta = minetest.get_meta(pos)
  60. local inv = meta:get_inventory()
  61. local inlist = inv:get_list("main")
  62. local typecnt = {}
  63. local typekeys = {}
  64. for _, st in ipairs(inlist) do
  65. if not st:is_empty() then
  66. local n = st:get_name()
  67. local k = string.format("%s", n)
  68. if not typecnt[k] then
  69. typecnt[k] = {st}
  70. table.insert(typekeys, k)
  71. else
  72. table.insert(typecnt[k], st)
  73. end
  74. end
  75. end
  76. table.sort(typekeys)
  77. inv:set_list("main", {})
  78. for _, k in ipairs(typekeys) do
  79. for _, item in ipairs(typecnt[k]) do
  80. inv:add_item("main", item)
  81. end
  82. end
  83. end
  84. local function get_share_names(meta)
  85. local share_names = meta:get_string("share_names") or ""
  86. if share_names ~= "" then
  87. local tb = minetest.parse_json(share_names)
  88. if tb and type(tb) == "table" then
  89. -- Count how many entries the share table has, and return that as second value.
  90. local c = 0
  91. for k, v in pairs(tb) do
  92. c = c + 1
  93. end
  94. return tb, c
  95. end
  96. end
  97. return {}, 0
  98. end
  99. chest_api.get_chest_formspec = function(name, desc, pos)
  100. local spos = pos.x .. "," .. pos.y .. "," .. pos.z
  101. local defgui = default.gui_bg ..
  102. default.gui_bg_img ..
  103. default.gui_slots
  104. local formspec
  105. local meta = minetest.get_meta(pos)
  106. local inv = meta:get_inventory()
  107. -- Permit grandfathering of old shared ironside chests.
  108. local shares, sharecount = get_share_names(meta)
  109. -- Obtain hooks into the trash mod's trash slot inventory.
  110. local ltrash, mtrash = trash.get_listname()
  111. local itrash = trash.get_iconname()
  112. -- Locked or unlocked gold chest.
  113. if string.find(name, "gold") then
  114. formspec = "size[12,10]" .. defgui ..
  115. "list[nodemeta:" .. spos .. ";main;0,1.3;12,4;]" ..
  116. "list[current_player;main;1,5.85;8,1;]" ..
  117. "list[current_player;main;1,7.08;8,3;8]" ..
  118. "listring[nodemeta:" .. spos .. ";main]" ..
  119. "listring[current_player;main]" ..
  120. "label[0,0;" .. desc .. "]" ..
  121. default.get_hotbar_bg(1, 5.85)
  122. -- Trash icon.
  123. .. "list[" .. ltrash .. ";" .. mtrash .. ";10,5.85;1,1;]" ..
  124. "image[10,5.85;1,1;" .. itrash .. "]"
  125. -- Locked gold chest.
  126. if string.find(name, "locked") then
  127. local chest_name = meta:get_string("chest_name") or ""
  128. formspec = formspec .. "button[10,0;2,1;rename;Rename]" ..
  129. "field[8.25,0.45;2,0.75;name;;]" ..
  130. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]"
  131. end
  132. -- Locked or unlocked diamond chest.
  133. elseif string.find(name, "diamond") then
  134. formspec = "size[12,11]" .. defgui ..
  135. "list[nodemeta:" .. spos .. ";main;0,1.3;12,5;]" ..
  136. "list[current_player;main;1,6.85;8,1;]" ..
  137. "list[current_player;main;1,8.08;8,3;8]" ..
  138. "listring[nodemeta:" .. spos .. ";main]" ..
  139. "listring[current_player;main]" ..
  140. "label[0,0;" .. desc .. "]" ..
  141. default.get_hotbar_bg(1, 6.85)
  142. -- Trash icon.
  143. .. "list[" .. ltrash .. ";" .. mtrash .. ";10,6.85;1,1;]" ..
  144. "image[10,6.85;1,1;" .. itrash .. "]"
  145. -- Locked diamond chest.
  146. if string.find(name, "locked") then
  147. local chest_name = meta:get_string("chest_name") or ""
  148. formspec = formspec .. "button[10,0;2,1;rename;Rename]" ..
  149. "field[8.25,0.45;2,0.75;name;;]" ..
  150. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]"
  151. end
  152. -- Locked or unlocked mithril chest.
  153. elseif string.find(name, "mithril") then
  154. formspec = "size[14,11]" .. defgui ..
  155. "list[nodemeta:" .. spos .. ";main;0,1.3;14,5;]" ..
  156. "list[current_player;main;2,6.85;8,1;]" ..
  157. "list[current_player;main;2,8.08;8,3;8]" ..
  158. "listring[nodemeta:" .. spos .. ";main]" ..
  159. "listring[current_player;main]" ..
  160. "label[0,0;" .. desc .. "]" ..
  161. default.get_hotbar_bg(2, 6.85)
  162. -- Trash icon.
  163. .. "list[" .. ltrash .. ";" .. mtrash .. ";11,6.85;1,1;]" ..
  164. "image[11,6.85;1,1;" .. itrash .. "]"
  165. -- Locked mithril chest.
  166. if string.find(name, "locked") then
  167. local chest_name = meta:get_string("chest_name") or ""
  168. formspec = formspec .. "button[12,0;2,1;rename;Rename]" ..
  169. "field[10.25,0.45;2,0.75;name;;]" ..
  170. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]"
  171. end
  172. -- Locked silver chest. (This chest is shareable.) Grandfather in old ironside chests.
  173. elseif (string.find(name, "silver") and string.find(name, "locked")) or sharecount > 0 then
  174. local chest_name = meta:get_string("chest_name") or ""
  175. formspec = "size[10,10]" .. defgui ..
  176. "list[nodemeta:" .. spos .. ";main;0,1.3;8,4;]" ..
  177. "list[current_player;main;0,5.85;8,1;]" ..
  178. "list[current_player;main;0,7.08;8,3;8]" ..
  179. "listring[nodemeta:" .. spos .. ";main]" ..
  180. "listring[current_player;main]" ..
  181. "label[0,0;" .. desc .. "]" ..
  182. default.get_hotbar_bg(0, 5.85) ..
  183. "button[6,0;2,1;rename;Rename]" ..
  184. "field[4.25,0.45;2,0.75;name;;]" ..
  185. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]" ..
  186. "button[8,1.2;2,1;doshare;Share Chest]" ..
  187. "button[8,2.2;2,1;unshare;Unshare]"
  188. -- Trash icon.
  189. .. "list[" .. ltrash .. ";" .. mtrash .. ";9,5.85;1,1;]" ..
  190. "image[9,5.85;1,1;" .. itrash .. "]"
  191. --formspec = formspec .. "textlist[8,1.26;1.70,3;sharelist;Item ##1,Item ##2,Item ##3]"
  192. -- Locked/unlocked copper, iron, or silver chests.
  193. elseif string.find(name, "iron") or
  194. string.find(name, "copper") or
  195. string.find(name, "silver") then
  196. local locked = string.find(name, "locked")
  197. if locked then
  198. formspec = "size[9,10]"
  199. else
  200. formspec = "size[8,10]"
  201. end
  202. formspec = formspec .. defgui ..
  203. "list[nodemeta:" .. spos .. ";main;0,1.3;8,4;]" ..
  204. "list[current_player;main;0,5.85;8,1;]" ..
  205. "list[current_player;main;0,7.08;8,3;8]" ..
  206. "listring[nodemeta:" .. spos .. ";main]" ..
  207. "listring[current_player;main]" ..
  208. "label[0,0;" .. desc .. "]" ..
  209. default.get_hotbar_bg(0, 5.85)
  210. -- Locked copper or iron chest.
  211. -- (If chest was locked silver, then another if-statement already handled it.)
  212. -- Iron locked chests with existing shares are grandfathered in.
  213. if locked then
  214. local chest_name = meta:get_string("chest_name") or ""
  215. formspec = formspec .. "button[6,0;2,1;rename;Rename]" ..
  216. "field[4.25,0.45;2,0.75;name;;]" ..
  217. "label[0,0.35;Label: <" .. minetest.formspec_escape(chest_name) .. ">]"
  218. -- Trash icon.
  219. .. "list[" .. ltrash .. ";" .. mtrash .. ";8,1.3;1,1;]" ..
  220. "image[8,1.3;1,1;" .. itrash .. "]"
  221. else
  222. -- Trash icon. This also applies to the unlocked silver chest!
  223. formspec = formspec
  224. .. "list[" .. ltrash .. ";" .. mtrash .. ";7,0;1,1;]" ..
  225. "image[7,0;1,1;" .. itrash .. "]"
  226. end
  227. -- Default chest or woodchest (old version). We MUST preserve the formspec for the old chest version!
  228. elseif inv:get_size("main") == 8*4 and (name:find("woodchest") or name:find("^chests:chest_")) then
  229. formspec = "size[8,9]" .. defgui ..
  230. "list[nodemeta:" .. spos .. ";main;0,0.3;8,4;]" ..
  231. "list[current_player;main;0,4.85;8,1;]" ..
  232. "list[current_player;main;0,6.08;8,3;8]" ..
  233. "listring[nodemeta:" .. spos .. ";main]" ..
  234. "listring[current_player;main]" ..
  235. default.get_hotbar_bg(0, 4.85)
  236. -- Locked/unlocked non-metalic chest (new version). Should have 8*3 size inventory.
  237. else
  238. formspec = "size[9,8]" .. defgui ..
  239. "list[nodemeta:" .. spos .. ";main;0,0.3;8,3;]" ..
  240. "list[current_player;main;0,3.85;8,1;]" ..
  241. "list[current_player;main;0,5.08;8,3;8]" ..
  242. "listring[nodemeta:" .. spos .. ";main]" ..
  243. "listring[current_player;main]" ..
  244. default.get_hotbar_bg(0, 3.85)
  245. -- Trash icon.
  246. .. "list[" .. ltrash .. ";" .. mtrash .. ";8,0.3;1,1;]" ..
  247. "image[8,0.3;1,1;" .. itrash .. "]"
  248. end
  249. return formspec
  250. end
  251. local function get_chest_name(meta)
  252. local cname = meta:get_string("chest_name") or ""
  253. if cname == "" then
  254. return "this chest"
  255. else
  256. return "<" .. cname .. ">"
  257. end
  258. end
  259. local function add_share_name(meta, name, pname)
  260. if name == "" then return end -- Failsafe.
  261. name = rename.grn(name)
  262. local share_names, share_count = get_share_names(meta)
  263. if not share_names[name] then
  264. minetest.chat_send_player(pname, "# Server: Player <" .. rename.gpn(name) .. "> will now have access to " .. get_chest_name(meta) .. ".")
  265. end
  266. share_names[name] = 1
  267. local str = minetest.write_json(share_names)
  268. meta:set_string("share_names", str)
  269. end
  270. local function del_share_name(meta, name, pname)
  271. if name == "" then return end -- Failsafe.
  272. name = rename.grn(name)
  273. local share_names, share_count = get_share_names(meta)
  274. if share_names[name] then
  275. minetest.chat_send_player(pname, "# Server: Player <" .. rename.gpn(name) .. "> can no longer access " .. get_chest_name(meta) .. ".")
  276. end
  277. share_names[name] = nil
  278. local str = minetest.write_json(share_names) -- Returns nil for empty table?
  279. meta:set_string("share_names", str)
  280. end
  281. -- Generate a share formspec. We need the chest metadata.
  282. chest_api.get_share_formspec = function(pos, meta)
  283. local node = minetest.get_node(pos)
  284. local nn = node.name
  285. local desc = minetest.reg_ns_nodes[nn].description
  286. local cname = meta:get_string("chest_name") or ""
  287. local formspec
  288. local defgui = default.gui_bg ..
  289. default.gui_bg_img ..
  290. default.gui_slots
  291. formspec = "size[8,5]" .. defgui ..
  292. "label[0,0;" .. minetest.formspec_escape(utility.get_short_desc(desc)) .. "]" ..
  293. "label[0,0.35;Label: <" .. minetest.formspec_escape(cname) .. ">]" ..
  294. "button[6,0;2,1;unshare;Unshare]" ..
  295. "button_exit[6,4;2,1;quit;Close]" ..
  296. "button[2.5,2.14;2,1.02;addname;Add Name]" ..
  297. "button[2.5,3.14;2,1.02;delname;Remove Name]" ..
  298. "label[0,1.71;Add or remove access grants:]" ..
  299. "field[0.27,2.46;2.5,1;addname_field;;]" ..
  300. "field[0.27,3.46;2.5,1;delname_field;;]" ..
  301. "label[0,4;Tip: any locked chest can be shared with a key.]"
  302. formspec = formspec ..
  303. "textlist[5,1;2.8,2.9;sharelist;"
  304. local share_names, share_count = get_share_names(meta)
  305. for k, v in pairs(share_names) do
  306. formspec = formspec .. minetest.formspec_escape(rename.gpn(k)) .. ","
  307. end
  308. formspec = string.gsub(formspec, ",$", "") -- Remove trailing comma.
  309. formspec = formspec .. "]"
  310. return formspec
  311. end
  312. -- This function is only used to check if a player is allowed to take/move/put
  313. -- from a chest inventory, or is allowed to open the formspec.
  314. local function has_locked_chest_privilege(pos, name, meta, player)
  315. if minetest.check_player_privs(player, "protection_bypass") then
  316. return true
  317. end
  318. if player:get_player_name() == meta:get_string("owner") then
  319. -- Owner can access the node to any time
  320. return true
  321. end
  322. -- Registered cheaters lose locked chest privileges.
  323. if sheriff.is_cheater(meta:get_string("owner")) then
  324. return true
  325. end
  326. -- Locked silver chests have sharing functionality. Remember to grandfather in old shared ironside chests.
  327. if name:find("iron") or name:find("silver") then
  328. local share_names, share_count = get_share_names(meta)
  329. if (string.find(name, "silver") and string.find(name, "locked")) or share_count > 0 then
  330. if share_names[player:get_player_name()] then
  331. return true
  332. end
  333. end
  334. end
  335. -- Is player wielding the right key?
  336. local item = player:get_wielded_item()
  337. if item:get_name() == "key:key" or item:get_name() == "key:chain" then
  338. local key_meta = item:get_meta()
  339. if key_meta:get_string("secret") == "" then
  340. local key_oldmeta = item:get_metadata()
  341. if key_oldmeta == "" or not minetest.parse_json(key_oldmeta) then
  342. return false
  343. end
  344. key_meta:set_string("secret", minetest.parse_json(key_oldmeta).secret)
  345. item:set_metadata("")
  346. end
  347. return meta:get_string("key_lock_secret") == key_meta:get_string("secret")
  348. end
  349. -- Is chest open?
  350. do
  351. local node = minetest.get_node(pos)
  352. if string.find(node.name, "_open$") then
  353. -- Player must be near enough to the chest.
  354. if vector_distance(pos, player:get_pos()) < 6 then
  355. return true
  356. end
  357. end
  358. end
  359. return false
  360. end
  361. local function chest_lid_obstructed(pos)
  362. local above = { x = pos.x, y = pos.y + 1, z = pos.z }
  363. local def = minetest.reg_ns_nodes[minetest.get_node(above).name]
  364. if not def then
  365. return true
  366. end
  367. -- allow ladders, signs, wallmounted things and torches to not obstruct
  368. if def.drawtype == "airlike" or
  369. def.drawtype == "signlike" or
  370. def.drawtype == "torchlike" or
  371. ((def.drawtype == "nodebox" or def.drawtype == "mesh") and def.paramtype2 == "wallmounted") then
  372. return false
  373. end
  374. return true
  375. end
  376. chest_api.open_chests = chest_api.open_chests or {}
  377. local open_chests = chest_api.open_chests
  378. local function open_chest(def, pos, node, clicker)
  379. -- Player name.
  380. local pname = clicker:get_player_name()
  381. -- Chest basename.
  382. local name = def._chest_basename
  383. -- Delay before opening chest formspec.
  384. local admin = (gdac.player_is_admin(pname) and gdac_invis.is_invisible(pname))
  385. local open_delay = (not admin and 0.2 or 0)
  386. -- Don't play sound or open chest, if opener is admin and not invisible.
  387. if not admin then
  388. local meta = minetest.get_meta(pos)
  389. local last_oiled = meta:get_int("oiled_time")
  390. if (os.time() - last_oiled) > math_random(0, 60*60*24*30) then
  391. -- Play sound, open chest.
  392. ambiance.sound_play(def.sound_open, pos, 0.5, 20)
  393. else
  394. -- Oiled chests open faster.
  395. open_delay = 0
  396. end
  397. if not chest_lid_obstructed(pos) then
  398. minetest.swap_node(pos,
  399. { name = name .. "_open",
  400. param2 = node.param2 })
  401. end
  402. end
  403. minetest.after(open_delay, minetest.show_formspec,
  404. clicker:get_player_name(),
  405. "default:chest",
  406. chest_api.get_chest_formspec(name, def.description, pos))
  407. open_chests[pname] = {
  408. pos = pos,
  409. sound = def.sound_close,
  410. swap = name .. "_closed",
  411. orig = name .. "_open",
  412. }
  413. end
  414. local function close_chest(pn, pos, node, swap, sound)
  415. -- Don't play sound or open chest, if opener is admin and not invisible.
  416. local admin = (gdac.player_is_admin(pn) and gdac_invis.is_invisible(pn))
  417. open_chests[pn] = nil
  418. -- Skip sorting, sound, and chest-closing, if player is invisible admin.
  419. if admin then
  420. return
  421. end
  422. for k, v in pairs(open_chests) do
  423. if vector.equals(v.pos, pos) then
  424. return -- This checks if someone else has chest open.
  425. end
  426. end
  427. -- Play sound, close chest.
  428. minetest.after(0.2, minetest.swap_node, pos, {
  429. name = swap,
  430. param2 = node.param2
  431. })
  432. local meta = minetest.get_meta(pos)
  433. local last_oiled = meta:get_int("oiled_time")
  434. if (os.time() - last_oiled) > math_random(0, 60*60*24*30) then
  435. ambiance.sound_play(sound, pos, 0.5, 20)
  436. end
  437. -- Mithril chests auto-sort when closed.
  438. -- Thus they are always already sorted when opened.
  439. if string.find(swap, "mithril") then
  440. chest_api.sort_chest(pos)
  441. end
  442. end
  443. function chest_api.on_leaveplayer(player, timeout)
  444. if not player or not player:is_player() then return end
  445. local pn = player:get_player_name()
  446. if open_chests[pn] then
  447. local pos = open_chests[pn].pos
  448. local node = minetest.get_node(pos)
  449. local sound = open_chests[pn].sound
  450. local swap = open_chests[pn].swap
  451. close_chest(pn, pos, node, swap, sound)
  452. end
  453. end
  454. function chest_api.on_dieplayer(player)
  455. if not player or not player:is_player() then return end
  456. local pn = player:get_player_name()
  457. if open_chests[pn] then
  458. local pos = open_chests[pn].pos
  459. local node = minetest.get_node(pos)
  460. local sound = open_chests[pn].sound
  461. local swap = open_chests[pn].swap
  462. close_chest(pn, pos, node, swap, sound)
  463. end
  464. end
  465. function chest_api.on_player_receive_fields(player, formname, fields)
  466. -- This function is valid for these callbacks only.
  467. if formname ~= "default:chest" and formname ~= "default:chest_share" then
  468. return -- Continue handling callbacks.
  469. end
  470. if not player then
  471. return true
  472. end
  473. local pn = player:get_player_name()
  474. -- Anticheat check.
  475. if not open_chests[pn] then
  476. minetest.chat_send_player(pn, "# Server: No chest opened.")
  477. return true -- Abort.
  478. end
  479. local pos = open_chests[pn].pos
  480. local node = minetest.get_node(pos)
  481. local nn = node.name
  482. local sound = open_chests[pn].sound
  483. local swap = open_chests[pn].swap
  484. local orig = open_chests[pn].orig
  485. local meta = minetest.get_meta(pos)
  486. local owner = meta:get_string("owner") or "" -- Only locked chests have owners.
  487. -- Failsafe.
  488. if nn ~= orig and nn ~= swap then
  489. minetest.chat_send_player(pn, "# Server: Error: 0xDEADBEEF (bad node)!")
  490. return true -- Abort.
  491. end
  492. if fields.rename then
  493. -- Anitcheat check.
  494. if (string.find(nn, "copper") or string.find(nn, "diamond") or
  495. string.find(nn, "iron") or
  496. string.find(nn, "silver") or
  497. string.find(nn, "gold") or
  498. string.find(nn, "mithril")) and string.find(nn, "locked") then
  499. if owner == pn or gdac.player_is_admin(pn) then
  500. minetest.chat_send_player(pn, "# Server: Chest name updated.")
  501. local new_name = (fields.name or "")
  502. new_name = new_name:trim()
  503. meta:set_string("chest_name", new_name)
  504. local owner = meta:get_string("owner") or ""
  505. local dname = rename.gpn(owner)
  506. local cname = meta:get_string("chest_name") or ""
  507. if cname == "" then
  508. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  509. else
  510. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  511. end
  512. local desc = minetest.reg_ns_nodes[nn].description
  513. minetest.show_formspec(pn, "default:chest", chest_api.get_chest_formspec(nn, desc, pos))
  514. else
  515. minetest.chat_send_player(pn, "# Server: You cannot relabel this chest.")
  516. end
  517. else
  518. minetest.chat_send_player(pn, "# Server: This chest does not have labeling functionality.")
  519. end
  520. end
  521. if fields.doshare then -- Button on main formspec.
  522. -- Permit grandfathering of old shared ironside chests.
  523. local shares, sharecount = get_share_names(meta)
  524. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  525. if owner == pn or gdac.player_is_admin(pn) then
  526. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  527. else
  528. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  529. end
  530. else
  531. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  532. end
  533. end
  534. if fields.unshare then -- This button is on the sharing and main formspecs.
  535. -- Permit grandfathering of old shared ironside chests.
  536. local shares, sharecount = get_share_names(meta)
  537. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  538. if owner == pn or gdac.player_is_admin(pn) then
  539. meta:set_string("share_names", nil)
  540. minetest.chat_send_player(pn, "# Server: All share grants revoked for " .. get_chest_name(meta) .. ".")
  541. -- Refresh sharing formspec only if already being displayed.
  542. -- The main formspec doesn't need updating.
  543. if formname == "default:chest_share" then
  544. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  545. end
  546. else
  547. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  548. end
  549. else
  550. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  551. end
  552. end
  553. if fields.addname and fields.addname_field ~= "" then -- Sharing formspec only.
  554. -- Permit grandfathering of old shared ironside chests.
  555. local shares, sharecount = get_share_names(meta)
  556. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  557. if owner == pn or gdac.player_is_admin(pn) then
  558. add_share_name(meta, fields.addname_field, pn)
  559. -- The sharing formspec is being displayed. Refresh it.
  560. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  561. else
  562. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  563. end
  564. else
  565. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  566. end
  567. end
  568. if fields.delname then -- Sharing formspec only.
  569. -- Permit grandfathering of old shared ironside chests.
  570. local shares, sharecount = get_share_names(meta)
  571. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  572. if owner == pn or gdac.player_is_admin(pn) then
  573. del_share_name(meta, fields.delname_field, pn)
  574. -- The sharing formspec is being displayed. Refresh it.
  575. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  576. else
  577. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  578. end
  579. else
  580. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  581. end
  582. end
  583. if not fields.quit then
  584. return true
  585. end
  586. -- Close chest.
  587. close_chest(pn, pos, node, swap, sound)
  588. return true
  589. end
  590. function chest_api.protected_on_construct(pos)
  591. local meta = minetest.get_meta(pos)
  592. meta:set_string("infotext", "Locked Chest")
  593. meta:set_string("owner", "")
  594. local inv = meta:get_inventory()
  595. local name = minetest.get_node(pos).name
  596. if string.find(name, "gold") then
  597. inv:set_size("main", 12*4)
  598. elseif string.find(name, "diamond") then
  599. inv:set_size("main", 12*5)
  600. elseif string.find(name, "mithril") then
  601. inv:set_size("main", 14*5)
  602. elseif name:find("woodchest") or name:find("^chests:chest_") then
  603. inv:set_size("main", 8*3)
  604. else
  605. inv:set_size("main", 8*4)
  606. end
  607. end
  608. function chest_api.protected_after_place_node(pos, placer)
  609. local meta = minetest.get_meta(pos)
  610. local owner = placer:get_player_name() or ""
  611. local dname = rename.gpn(owner)
  612. meta:set_string("owner", owner)
  613. meta:set_string("rename", dname)
  614. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  615. meta:set_string("formspec", nil)
  616. end
  617. function chest_api.protected_can_dig(pos, player)
  618. local meta = minetest.get_meta(pos)
  619. local owner = meta:get_string("owner") or ""
  620. local pname = player:get_player_name()
  621. local inv = meta:get_inventory()
  622. -- Only chest owners can dig shared locked chests.
  623. -- If chests is owned by the server, or by no one, then anyone can dig.
  624. return inv:is_empty("main") and (owner == pname or owner == "" or owner == "server")
  625. end
  626. function chest_api.protected_allow_metadata_inventory_move(
  627. pos, from_list, from_index, to_list, to_index, count, player)
  628. local meta = minetest.get_meta(pos)
  629. local name = minetest.get_node(pos).name
  630. if not has_locked_chest_privilege(pos, name, meta, player) then
  631. return 0
  632. end
  633. return count
  634. end
  635. function chest_api.protected_allow_metadata_inventory_put(
  636. pos, listname, index, stack, player)
  637. local meta = minetest.get_meta(pos)
  638. local name = minetest.get_node(pos).name
  639. if not has_locked_chest_privilege(pos, name, meta, player) then
  640. return 0
  641. end
  642. return stack:get_count()
  643. end
  644. function chest_api.protected_allow_metadata_inventory_take(
  645. pos, listname, index, stack, player)
  646. local meta = minetest.get_meta(pos)
  647. local name = minetest.get_node(pos).name
  648. if not has_locked_chest_privilege(pos, name, meta, player) then
  649. return 0
  650. end
  651. return stack:get_count()
  652. end
  653. function chest_api.protected_on_rightclick(pos, node, clicker)
  654. local meta = minetest.get_meta(pos)
  655. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  656. local name = def._chest_basename
  657. if not has_locked_chest_privilege(pos, name, meta, clicker) then
  658. ambiance.sound_play("default_chest_locked", pos, 1.0, 20)
  659. return
  660. end
  661. local meta = minetest.get_meta(pos)
  662. local owner = meta:get_string("owner") or ""
  663. local dname = rename.gpn(owner)
  664. local cname = meta:get_string("chest_name") or ""
  665. if cname == "" then
  666. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  667. else
  668. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  669. end
  670. -- Upgrade inventory size.
  671. if string.find(name, "gold") then
  672. local inv = meta:get_inventory()
  673. local inv_sz = inv:get_size("main")
  674. if inv_sz ~= 12*4 then
  675. inv:set_size("main", 12*4)
  676. end
  677. elseif string.find(name, "mithril") then
  678. local inv = meta:get_inventory()
  679. local inv_sz = inv:get_size("main")
  680. if inv_sz ~= 14*5 then
  681. inv:set_size("main", 14*5)
  682. end
  683. end
  684. -- Do NOT update inventory sides for old woodchests/default chests.
  685. -- They are relied on by too many players. Only new chests will have smaller inventories.
  686. open_chest(def, pos, node, clicker)
  687. end
  688. function chest_api.protected_on_key_use(pos, player)
  689. local node = minetest.get_node(pos)
  690. local def = minetest.reg_ns_nodes[node.name]
  691. local name = def._chest_basename
  692. -- Failsafe.
  693. if node.name ~= name .. "_closed" then return end
  694. if not player or not player:is_player() then return end
  695. local meta = minetest.get_meta(pos)
  696. local secret = meta:get_string("key_lock_secret")
  697. local itemstack = player:get_wielded_item()
  698. local key_meta = itemstack:get_meta()
  699. if key_meta:get_string("secret") == "" then
  700. local key_oldmeta = itemstack:get_metadata()
  701. if key_oldmeta == "" or not minetest.parse_json(key_oldmeta) then
  702. return false
  703. end
  704. key_meta:set_string("secret", minetest.parse_json(key_oldmeta).secret)
  705. itemstack:set_metadata("")
  706. end
  707. if secret ~= key_meta:get_string("secret") then
  708. minetest.chat_send_player(player:get_player_name(), "# Server: Key does not fit lock!")
  709. return
  710. end
  711. local owner = meta:get_string("owner") or ""
  712. local dname = rename.gpn(owner)
  713. local cname = meta:get_string("chest_name") or ""
  714. if cname == "" then
  715. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  716. else
  717. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  718. end
  719. -- Upgrade inventory size.
  720. if string.find(name, "gold") then
  721. local inv = meta:get_inventory()
  722. local inv_sz = inv:get_size("main")
  723. if inv_sz ~= 12*4 then
  724. inv:set_size("main", 12*4)
  725. end
  726. elseif string.find(name, "mithril") then
  727. local inv = meta:get_inventory()
  728. local inv_sz = inv:get_size("main")
  729. if inv_sz ~= 14*5 then
  730. inv:set_size("main", 14*5)
  731. end
  732. end
  733. -- Do NOT update inventory sides for old woodchests/default chests.
  734. -- They are relied on by too many players. Only new chests will have smaller inventories.
  735. open_chest(def, pos, node, player)
  736. end
  737. function chest_api.protected_on_skeleton_key_use(pos, player, newsecret)
  738. local meta = minetest.get_meta(pos)
  739. local owner = meta:get_string("owner")
  740. local pname = player:get_player_name()
  741. -- verify placer is owner of lockable chest
  742. if not gdac.player_is_admin(pname) then
  743. if owner ~= pname then
  744. minetest.record_protection_violation(pos, pname)
  745. minetest.chat_send_player(pname, "# Server: You do not own this chest.")
  746. return nil
  747. end
  748. end
  749. local secret = meta:get_string("key_lock_secret")
  750. if secret == "" then
  751. secret = newsecret
  752. meta:set_string("key_lock_secret", secret)
  753. end
  754. return secret, "a locked chest", owner
  755. end
  756. function chest_api.protected_on_rename_check(pos)
  757. local meta = minetest.get_meta(pos)
  758. local owner = meta:get_string("owner")
  759. -- Nobody placed this block.
  760. if owner == "" then
  761. return
  762. end
  763. local dname = rename.gpn(owner)
  764. meta:set_string("rename", dname)
  765. local label = meta:get_string("chest_name") or ""
  766. if label == "" then
  767. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  768. else
  769. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. label .. ">")
  770. end
  771. end
  772. function chest_api.public_on_construct(pos)
  773. local meta = minetest.get_meta(pos)
  774. local name = minetest.get_node(pos).name
  775. meta:set_string("infotext", "Unlocked Chest")
  776. local inv = meta:get_inventory()
  777. if string.find(name, "gold") then
  778. inv:set_size("main", 12*4)
  779. elseif string.find(name, "diamond") then
  780. inv:set_size("main", 12*5)
  781. elseif string.find(name, "mithril") then
  782. inv:set_size("main", 14*5)
  783. elseif name:find("woodchest") or name:find("^chests:chest_") then
  784. inv:set_size("main", 8*3)
  785. else
  786. inv:set_size("main", 8*4)
  787. end
  788. end
  789. function chest_api.public_can_dig(pos,player)
  790. local meta = minetest.get_meta(pos);
  791. local inv = meta:get_inventory()
  792. return inv:is_empty("main")
  793. end
  794. function chest_api.public_on_rightclick(pos, node, clicker)
  795. local meta = minetest.get_meta(pos)
  796. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  797. local name = def._chest_basename
  798. meta:set_string("infotext", "Unlocked Chest")
  799. meta:set_string("formspec", nil)
  800. -- Upgrade inventory size.
  801. if string.find(name, "gold") then
  802. local inv = meta:get_inventory()
  803. local inv_sz = inv:get_size("main")
  804. if inv_sz ~= 12*4 then
  805. inv:set_size("main", 12*4)
  806. end
  807. elseif string.find(name, "mithril") then
  808. local inv = meta:get_inventory()
  809. local inv_sz = inv:get_size("main")
  810. if inv_sz ~= 14*5 then
  811. inv:set_size("main", 14*5)
  812. end
  813. end
  814. -- Do NOT update inventory sides for old woodchests/default chests.
  815. -- They are relied on by too many players. Only new chests will have smaller inventories.
  816. open_chest(def, pos, node, clicker)
  817. end
  818. function chest_api.on_receive_fields(pos, formname, fields, sender)
  819. if not sender or not sender:is_player() then return end
  820. -- convert chest by removing the formspec and renaming the inv
  821. -- the order here is chosen to minimize the chance of two conversion
  822. -- attempts running at the same time from racing.
  823. -- after this function runs, it destroys the formspec in the metadata
  824. -- and will never be called again for this chest.
  825. local meta = minetest.get_meta(pos)
  826. meta:set_string("formspec", nil)
  827. -- We do not attempt to switch the inventory.
  828. --local inv = meta:get_inventory()
  829. --local list = inv:get_list("main")
  830. --inv:set_list("main", nil)
  831. --inv:set_size("default:chest", 8*4)
  832. --inv:set_list("default:chest", list)
  833. end
  834. function chest_api.on_metadata_inventory_move(
  835. pos, from_list, from_index, to_list, to_index, count, player)
  836. minetest.log("action", player:get_player_name() ..
  837. " moves stuff in chest at " .. minetest.pos_to_string(pos))
  838. if string.find(minetest.get_node(pos).name, "locked") then
  839. minetest.after(0, function() chest_api.update_vending(pos) end)
  840. end
  841. end
  842. function chest_api.on_metadata_inventory_put(pos, listname, index, stack, player)
  843. minetest.log("action", player:get_player_name() ..
  844. " moves " .. stack:get_name() ..
  845. " to chest at " .. minetest.pos_to_string(pos))
  846. if string.find(minetest.get_node(pos).name, "locked") then
  847. minetest.after(0, function() chest_api.update_vending(pos) end)
  848. end
  849. end
  850. function chest_api.on_metadata_inventory_take(pos, listname, index, stack, player)
  851. minetest.log("action", player:get_player_name() ..
  852. " takes " .. stack:get_name() ..
  853. " from chest at " .. minetest.pos_to_string(pos))
  854. if string.find(minetest.get_node(pos).name, "locked") then
  855. minetest.after(0, function() chest_api.update_vending(pos) end)
  856. end
  857. end
  858. function chest_api.on_blast(pos)
  859. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  860. local name = def._chest_basename
  861. local drops = {}
  862. default.get_inventory_drops(pos, "main", drops)
  863. drops[#drops+1] = name .. "_closed"
  864. minetest.remove_node(pos)
  865. return drops
  866. end