functions.lua 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  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. --
  324. -- Note: disabled as of [8/15/22]: the consequences of an accidental false
  325. -- positive include (but are not limited to): drama, emoting, rage-quitting,
  326. -- misunderstanding, fake news, incorrect information, assumptions, "stolen"
  327. -- items, returned items, banned items, items on fire, gossip, problems with
  328. -- family members, tale-bearing, suspicions that the admin is taking sides,
  329. -- unhelpful sisters, locked chests, unlocked chests, chests with stray pieces
  330. -- of underwear, missing time, verbal combat and other general mayhem, etc.
  331. -- etc. etc.
  332. --
  333. -- Update: re-enabled as of [8/16/22]: there is now a delay which allows the
  334. -- admin time to correct a mistake in the event of a wrong cheat detection.
  335. do
  336. local cheater, time = sheriff.is_cheater(meta:get_string("owner"))
  337. if cheater then
  338. local week = 60*60*24*7
  339. if os.time() > (time + week) then
  340. return true
  341. end
  342. end
  343. end
  344. -- Locked silver chests have sharing functionality. Remember to grandfather in old shared ironside chests.
  345. if name:find("iron") or name:find("silver") then
  346. local share_names, share_count = get_share_names(meta)
  347. if (string.find(name, "silver") and string.find(name, "locked")) or share_count > 0 then
  348. if share_names[player:get_player_name()] then
  349. return true
  350. end
  351. end
  352. end
  353. -- Is player wielding the right key?
  354. local item = player:get_wielded_item()
  355. if item:get_name() == "key:key" or item:get_name() == "key:chain" then
  356. local key_meta = item:get_meta()
  357. if key_meta:get_string("secret") == "" then
  358. local key_oldmeta = item:get_metadata()
  359. if key_oldmeta == "" or not minetest.parse_json(key_oldmeta) then
  360. return false
  361. end
  362. key_meta:set_string("secret", minetest.parse_json(key_oldmeta).secret)
  363. item:set_metadata("")
  364. end
  365. return meta:get_string("key_lock_secret") == key_meta:get_string("secret")
  366. end
  367. -- Is chest open?
  368. do
  369. local node = minetest.get_node(pos)
  370. if string.find(node.name, "_open$") then
  371. -- Player must be near enough to the chest.
  372. if vector_distance(pos, player:get_pos()) < 6 then
  373. return true
  374. end
  375. end
  376. end
  377. return false
  378. end
  379. local function chest_lid_obstructed(pos)
  380. local above = { x = pos.x, y = pos.y + 1, z = pos.z }
  381. local def = minetest.reg_ns_nodes[minetest.get_node(above).name]
  382. if not def then
  383. return true
  384. end
  385. -- allow ladders, signs, wallmounted things and torches to not obstruct
  386. if def.drawtype == "airlike" or
  387. def.drawtype == "signlike" or
  388. def.drawtype == "torchlike" or
  389. ((def.drawtype == "nodebox" or def.drawtype == "mesh") and def.paramtype2 == "wallmounted") then
  390. return false
  391. end
  392. return true
  393. end
  394. chest_api.open_chests = chest_api.open_chests or {}
  395. local open_chests = chest_api.open_chests
  396. local function open_chest(def, pos, node, clicker)
  397. -- Player name.
  398. local pname = clicker:get_player_name()
  399. -- Chest basename.
  400. local name = def._chest_basename
  401. -- Delay before opening chest formspec.
  402. local admin = (gdac.player_is_admin(pname) and gdac_invis.is_invisible(pname))
  403. local open_delay = (not admin and 0.2 or 0)
  404. -- Don't play sound or open chest, if opener is admin and not invisible.
  405. if not admin then
  406. local meta = minetest.get_meta(pos)
  407. local last_oiled = meta:get_int("oiled_time")
  408. if (os.time() - last_oiled) > math_random(0, 60*60*24*30) then
  409. -- Play sound, open chest.
  410. ambiance.sound_play(def.sound_open, pos, 0.5, 20)
  411. else
  412. -- Oiled chests open faster.
  413. open_delay = 0
  414. end
  415. if not chest_lid_obstructed(pos) then
  416. minetest.swap_node(pos,
  417. { name = name .. "_open",
  418. param2 = node.param2 })
  419. end
  420. end
  421. minetest.after(open_delay, minetest.show_formspec,
  422. clicker:get_player_name(),
  423. "default:chest",
  424. chest_api.get_chest_formspec(name, def.description, pos))
  425. open_chests[pname] = {
  426. pos = pos,
  427. sound = def.sound_close,
  428. swap = name .. "_closed",
  429. orig = name .. "_open",
  430. }
  431. end
  432. local function close_chest(pn, pos, node, swap, sound)
  433. -- Don't play sound or open chest, if opener is admin and not invisible.
  434. local admin = (gdac.player_is_admin(pn) and gdac_invis.is_invisible(pn))
  435. open_chests[pn] = nil
  436. -- Skip sorting, sound, and chest-closing, if player is invisible admin.
  437. if admin then
  438. return
  439. end
  440. for k, v in pairs(open_chests) do
  441. if vector.equals(v.pos, pos) then
  442. return -- This checks if someone else has chest open.
  443. end
  444. end
  445. -- Play sound, close chest.
  446. minetest.after(0.2, minetest.swap_node, pos, {
  447. name = swap,
  448. param2 = node.param2
  449. })
  450. local meta = minetest.get_meta(pos)
  451. local last_oiled = meta:get_int("oiled_time")
  452. if (os.time() - last_oiled) > math_random(0, 60*60*24*30) then
  453. ambiance.sound_play(sound, pos, 0.5, 20)
  454. end
  455. -- Mithril chests auto-sort when closed.
  456. -- Thus they are always already sorted when opened.
  457. if string.find(swap, "mithril") then
  458. chest_api.sort_chest(pos)
  459. end
  460. end
  461. function chest_api.on_leaveplayer(player, timeout)
  462. if not player or not player:is_player() then return end
  463. local pn = player:get_player_name()
  464. if open_chests[pn] then
  465. local pos = open_chests[pn].pos
  466. local node = minetest.get_node(pos)
  467. local sound = open_chests[pn].sound
  468. local swap = open_chests[pn].swap
  469. close_chest(pn, pos, node, swap, sound)
  470. end
  471. end
  472. function chest_api.on_dieplayer(player)
  473. if not player or not player:is_player() then return end
  474. local pn = player:get_player_name()
  475. if open_chests[pn] then
  476. local pos = open_chests[pn].pos
  477. local node = minetest.get_node(pos)
  478. local sound = open_chests[pn].sound
  479. local swap = open_chests[pn].swap
  480. close_chest(pn, pos, node, swap, sound)
  481. end
  482. end
  483. function chest_api.on_player_receive_fields(player, formname, fields)
  484. -- This function is valid for these callbacks only.
  485. if formname ~= "default:chest" and formname ~= "default:chest_share" then
  486. return -- Continue handling callbacks.
  487. end
  488. if not player then
  489. return true
  490. end
  491. local pn = player:get_player_name()
  492. -- Anticheat check.
  493. if not open_chests[pn] then
  494. minetest.chat_send_player(pn, "# Server: No chest opened.")
  495. return true -- Abort.
  496. end
  497. local pos = open_chests[pn].pos
  498. local node = minetest.get_node(pos)
  499. local nn = node.name
  500. local sound = open_chests[pn].sound
  501. local swap = open_chests[pn].swap
  502. local orig = open_chests[pn].orig
  503. local meta = minetest.get_meta(pos)
  504. local owner = meta:get_string("owner") or "" -- Only locked chests have owners.
  505. -- Failsafe.
  506. if nn ~= orig and nn ~= swap then
  507. minetest.chat_send_player(pn, "# Server: Error: 0xDEADBEEF (bad node)!")
  508. return true -- Abort.
  509. end
  510. if fields.rename then
  511. -- Anitcheat check.
  512. if (string.find(nn, "copper") or string.find(nn, "diamond") or
  513. string.find(nn, "iron") or
  514. string.find(nn, "silver") or
  515. string.find(nn, "gold") or
  516. string.find(nn, "mithril")) and string.find(nn, "locked") then
  517. if owner == pn or gdac.player_is_admin(pn) then
  518. minetest.chat_send_player(pn, "# Server: Chest name updated.")
  519. local new_name = (fields.name or "")
  520. new_name = new_name:trim()
  521. meta:set_string("chest_name", new_name)
  522. local owner = meta:get_string("owner") or ""
  523. local dname = rename.gpn(owner)
  524. local cname = meta:get_string("chest_name") or ""
  525. if cname == "" then
  526. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  527. else
  528. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  529. end
  530. local desc = minetest.reg_ns_nodes[nn].description
  531. minetest.show_formspec(pn, "default:chest", chest_api.get_chest_formspec(nn, desc, pos))
  532. else
  533. minetest.chat_send_player(pn, "# Server: You cannot relabel this chest.")
  534. end
  535. else
  536. minetest.chat_send_player(pn, "# Server: This chest does not have labeling functionality.")
  537. end
  538. end
  539. if fields.doshare then -- Button on main formspec.
  540. -- Permit grandfathering of old shared ironside chests.
  541. local shares, sharecount = get_share_names(meta)
  542. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  543. if owner == pn or gdac.player_is_admin(pn) then
  544. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  545. else
  546. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  547. end
  548. else
  549. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  550. end
  551. end
  552. if fields.unshare then -- This button is on the sharing and main formspecs.
  553. -- Permit grandfathering of old shared ironside chests.
  554. local shares, sharecount = get_share_names(meta)
  555. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  556. if owner == pn or gdac.player_is_admin(pn) then
  557. meta:set_string("share_names", nil)
  558. minetest.chat_send_player(pn, "# Server: All share grants revoked for " .. get_chest_name(meta) .. ".")
  559. -- Refresh sharing formspec only if already being displayed.
  560. -- The main formspec doesn't need updating.
  561. if formname == "default:chest_share" then
  562. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  563. end
  564. else
  565. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  566. end
  567. else
  568. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  569. end
  570. end
  571. if fields.addname and fields.addname_field ~= "" then -- Sharing formspec only.
  572. -- Permit grandfathering of old shared ironside chests.
  573. local shares, sharecount = get_share_names(meta)
  574. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  575. if owner == pn or gdac.player_is_admin(pn) then
  576. add_share_name(meta, fields.addname_field, pn)
  577. -- The sharing formspec is being displayed. Refresh it.
  578. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  579. else
  580. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  581. end
  582. else
  583. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  584. end
  585. end
  586. if fields.delname then -- Sharing formspec only.
  587. -- Permit grandfathering of old shared ironside chests.
  588. local shares, sharecount = get_share_names(meta)
  589. if (string.find(nn, "silver") and string.find(nn, "locked")) or sharecount > 0 then
  590. if owner == pn or gdac.player_is_admin(pn) then
  591. del_share_name(meta, fields.delname_field, pn)
  592. -- The sharing formspec is being displayed. Refresh it.
  593. minetest.show_formspec(pn, "default:chest_share", chest_api.get_share_formspec(pos, meta))
  594. else
  595. minetest.chat_send_player(pn, "# Server: You do not have permission to manage shares for this chest.")
  596. end
  597. else
  598. minetest.chat_send_player(pn, "# Server: This chest does not have sharing functionality.")
  599. end
  600. end
  601. if not fields.quit then
  602. return true
  603. end
  604. -- Close chest.
  605. close_chest(pn, pos, node, swap, sound)
  606. return true
  607. end
  608. function chest_api.protected_on_construct(pos)
  609. local meta = minetest.get_meta(pos)
  610. meta:set_string("infotext", "Locked Chest")
  611. meta:set_string("owner", "")
  612. local inv = meta:get_inventory()
  613. local name = minetest.get_node(pos).name
  614. if string.find(name, "gold") then
  615. inv:set_size("main", 12*4)
  616. elseif string.find(name, "diamond") then
  617. inv:set_size("main", 12*5)
  618. elseif string.find(name, "mithril") then
  619. inv:set_size("main", 14*5)
  620. elseif name:find("woodchest") or name:find("^chests:chest_") then
  621. inv:set_size("main", 8*3)
  622. else
  623. inv:set_size("main", 8*4)
  624. end
  625. end
  626. function chest_api.protected_after_place_node(pos, placer)
  627. local meta = minetest.get_meta(pos)
  628. local owner = placer:get_player_name() or ""
  629. local dname = rename.gpn(owner)
  630. meta:set_string("owner", owner)
  631. meta:set_string("rename", dname)
  632. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  633. meta:set_string("formspec", nil)
  634. end
  635. function chest_api.protected_can_dig(pos, player)
  636. local meta = minetest.get_meta(pos)
  637. local owner = meta:get_string("owner") or ""
  638. local pname = player:get_player_name()
  639. local inv = meta:get_inventory()
  640. -- Only chest owners can dig shared locked chests.
  641. -- If chests is owned by the server, or by no one, then anyone can dig.
  642. return inv:is_empty("main") and (owner == pname or owner == "" or owner == "server")
  643. end
  644. function chest_api.protected_allow_metadata_inventory_move(
  645. pos, from_list, from_index, to_list, to_index, count, 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 count
  652. end
  653. function chest_api.protected_allow_metadata_inventory_put(
  654. pos, listname, index, stack, player)
  655. local meta = minetest.get_meta(pos)
  656. local name = minetest.get_node(pos).name
  657. if not has_locked_chest_privilege(pos, name, meta, player) then
  658. return 0
  659. end
  660. return stack:get_count()
  661. end
  662. function chest_api.protected_allow_metadata_inventory_take(
  663. pos, listname, index, stack, player)
  664. local meta = minetest.get_meta(pos)
  665. local name = minetest.get_node(pos).name
  666. if not has_locked_chest_privilege(pos, name, meta, player) then
  667. return 0
  668. end
  669. return stack:get_count()
  670. end
  671. function chest_api.protected_on_rightclick(pos, node, clicker)
  672. local meta = minetest.get_meta(pos)
  673. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  674. local name = def._chest_basename
  675. if not has_locked_chest_privilege(pos, name, meta, clicker) then
  676. ambiance.sound_play("default_chest_locked", pos, 1.0, 20)
  677. return
  678. end
  679. local meta = minetest.get_meta(pos)
  680. local owner = meta:get_string("owner") or ""
  681. local dname = rename.gpn(owner)
  682. local cname = meta:get_string("chest_name") or ""
  683. if cname == "" then
  684. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  685. else
  686. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  687. end
  688. -- Upgrade inventory size.
  689. if string.find(name, "gold") then
  690. local inv = meta:get_inventory()
  691. local inv_sz = inv:get_size("main")
  692. if inv_sz ~= 12*4 then
  693. inv:set_size("main", 12*4)
  694. end
  695. elseif string.find(name, "mithril") then
  696. local inv = meta:get_inventory()
  697. local inv_sz = inv:get_size("main")
  698. if inv_sz ~= 14*5 then
  699. inv:set_size("main", 14*5)
  700. end
  701. end
  702. -- Do NOT update inventory sides for old woodchests/default chests.
  703. -- They are relied on by too many players. Only new chests will have smaller inventories.
  704. open_chest(def, pos, node, clicker)
  705. end
  706. function chest_api.protected_on_key_use(pos, player)
  707. local node = minetest.get_node(pos)
  708. local def = minetest.reg_ns_nodes[node.name]
  709. local name = def._chest_basename
  710. -- Failsafe.
  711. if node.name ~= name .. "_closed" then return end
  712. if not player or not player:is_player() then return end
  713. local meta = minetest.get_meta(pos)
  714. local secret = meta:get_string("key_lock_secret")
  715. local itemstack = player:get_wielded_item()
  716. local key_meta = itemstack:get_meta()
  717. if key_meta:get_string("secret") == "" then
  718. local key_oldmeta = itemstack:get_metadata()
  719. if key_oldmeta == "" or not minetest.parse_json(key_oldmeta) then
  720. return false
  721. end
  722. key_meta:set_string("secret", minetest.parse_json(key_oldmeta).secret)
  723. itemstack:set_metadata("")
  724. end
  725. if secret ~= key_meta:get_string("secret") then
  726. minetest.chat_send_player(player:get_player_name(), "# Server: Key does not fit lock!")
  727. return
  728. end
  729. local owner = meta:get_string("owner") or ""
  730. local dname = rename.gpn(owner)
  731. local cname = meta:get_string("chest_name") or ""
  732. if cname == "" then
  733. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  734. else
  735. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. cname .. ">")
  736. end
  737. -- Upgrade inventory size.
  738. if string.find(name, "gold") then
  739. local inv = meta:get_inventory()
  740. local inv_sz = inv:get_size("main")
  741. if inv_sz ~= 12*4 then
  742. inv:set_size("main", 12*4)
  743. end
  744. elseif string.find(name, "mithril") then
  745. local inv = meta:get_inventory()
  746. local inv_sz = inv:get_size("main")
  747. if inv_sz ~= 14*5 then
  748. inv:set_size("main", 14*5)
  749. end
  750. end
  751. -- Do NOT update inventory sides for old woodchests/default chests.
  752. -- They are relied on by too many players. Only new chests will have smaller inventories.
  753. open_chest(def, pos, node, player)
  754. end
  755. function chest_api.protected_on_skeleton_key_use(pos, player, newsecret)
  756. local meta = minetest.get_meta(pos)
  757. local owner = meta:get_string("owner")
  758. local pname = player:get_player_name()
  759. -- verify placer is owner of lockable chest
  760. if not gdac.player_is_admin(pname) then
  761. if owner ~= pname then
  762. minetest.record_protection_violation(pos, pname)
  763. minetest.chat_send_player(pname, "# Server: You do not own this chest.")
  764. return nil
  765. end
  766. end
  767. local secret = meta:get_string("key_lock_secret")
  768. if secret == "" then
  769. secret = newsecret
  770. meta:set_string("key_lock_secret", secret)
  771. end
  772. return secret, "a locked chest", owner
  773. end
  774. function chest_api.protected_on_rename_check(pos)
  775. local meta = minetest.get_meta(pos)
  776. local owner = meta:get_string("owner")
  777. -- Nobody placed this block.
  778. if owner == "" then
  779. return
  780. end
  781. local dname = rename.gpn(owner)
  782. meta:set_string("rename", dname)
  783. local label = meta:get_string("chest_name") or ""
  784. if label == "" then
  785. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)")
  786. else
  787. meta:set_string("infotext", "Locked Chest (Owned by <" .. dname .. ">!)\nLabel: <" .. label .. ">")
  788. end
  789. end
  790. function chest_api.public_on_construct(pos)
  791. local meta = minetest.get_meta(pos)
  792. local name = minetest.get_node(pos).name
  793. meta:set_string("infotext", "Unlocked Chest")
  794. local inv = meta:get_inventory()
  795. if string.find(name, "gold") then
  796. inv:set_size("main", 12*4)
  797. elseif string.find(name, "diamond") then
  798. inv:set_size("main", 12*5)
  799. elseif string.find(name, "mithril") then
  800. inv:set_size("main", 14*5)
  801. elseif name:find("woodchest") or name:find("^chests:chest_") then
  802. inv:set_size("main", 8*3)
  803. else
  804. inv:set_size("main", 8*4)
  805. end
  806. end
  807. function chest_api.public_can_dig(pos,player)
  808. local meta = minetest.get_meta(pos);
  809. local inv = meta:get_inventory()
  810. return inv:is_empty("main")
  811. end
  812. function chest_api.public_on_rightclick(pos, node, clicker)
  813. local meta = minetest.get_meta(pos)
  814. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  815. local name = def._chest_basename
  816. meta:set_string("infotext", "Unlocked Chest")
  817. meta:set_string("formspec", nil)
  818. -- Upgrade inventory size.
  819. if string.find(name, "gold") then
  820. local inv = meta:get_inventory()
  821. local inv_sz = inv:get_size("main")
  822. if inv_sz ~= 12*4 then
  823. inv:set_size("main", 12*4)
  824. end
  825. elseif string.find(name, "mithril") then
  826. local inv = meta:get_inventory()
  827. local inv_sz = inv:get_size("main")
  828. if inv_sz ~= 14*5 then
  829. inv:set_size("main", 14*5)
  830. end
  831. end
  832. -- Do NOT update inventory sides for old woodchests/default chests.
  833. -- They are relied on by too many players. Only new chests will have smaller inventories.
  834. open_chest(def, pos, node, clicker)
  835. end
  836. function chest_api.on_receive_fields(pos, formname, fields, sender)
  837. if not sender or not sender:is_player() then return end
  838. -- convert chest by removing the formspec and renaming the inv
  839. -- the order here is chosen to minimize the chance of two conversion
  840. -- attempts running at the same time from racing.
  841. -- after this function runs, it destroys the formspec in the metadata
  842. -- and will never be called again for this chest.
  843. local meta = minetest.get_meta(pos)
  844. meta:set_string("formspec", nil)
  845. -- We do not attempt to switch the inventory.
  846. --local inv = meta:get_inventory()
  847. --local list = inv:get_list("main")
  848. --inv:set_list("main", nil)
  849. --inv:set_size("default:chest", 8*4)
  850. --inv:set_list("default:chest", list)
  851. end
  852. function chest_api.on_metadata_inventory_move(
  853. pos, from_list, from_index, to_list, to_index, count, player)
  854. minetest.log("action", player:get_player_name() ..
  855. " moves stuff in chest at " .. minetest.pos_to_string(pos))
  856. if string.find(minetest.get_node(pos).name, "locked") then
  857. minetest.after(0, function() chest_api.update_vending(pos) end)
  858. end
  859. end
  860. function chest_api.on_metadata_inventory_put(pos, listname, index, stack, player)
  861. minetest.log("action", player:get_player_name() ..
  862. " moves " .. stack:get_name() ..
  863. " to chest at " .. minetest.pos_to_string(pos))
  864. if string.find(minetest.get_node(pos).name, "locked") then
  865. minetest.after(0, function() chest_api.update_vending(pos) end)
  866. end
  867. end
  868. function chest_api.on_metadata_inventory_take(pos, listname, index, stack, player)
  869. minetest.log("action", player:get_player_name() ..
  870. " takes " .. stack:get_name() ..
  871. " from chest at " .. minetest.pos_to_string(pos))
  872. if string.find(minetest.get_node(pos).name, "locked") then
  873. minetest.after(0, function() chest_api.update_vending(pos) end)
  874. end
  875. end
  876. function chest_api.on_blast(pos)
  877. local def = minetest.reg_ns_nodes[minetest.get_node(pos).name]
  878. local name = def._chest_basename
  879. local drops = {}
  880. default.get_inventory_drops(pos, "main", drops)
  881. drops[#drops+1] = name .. "_closed"
  882. minetest.remove_node(pos)
  883. return drops
  884. end