A rewrite of the node_ownership Minetest mod with many new features. https://content.minetest.net/packages/ShadowNinja/areas/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

445 lines
12KB

  1. local S = minetest.get_translator("areas")
  2. minetest.register_chatcommand("protect", {
  3. params = S("<AreaName>"),
  4. description = S("Protect your own area"),
  5. privs = {[areas.config.self_protection_privilege]=true},
  6. func = function(name, param)
  7. if param == "" then
  8. return false, S("Invalid usage, see /help @1.", "protect")
  9. end
  10. local pos1, pos2 = areas:getPos(name)
  11. if not (pos1 and pos2) then
  12. return false, S("You need to select an area first.")
  13. end
  14. minetest.log("action", "/protect invoked, owner="..name..
  15. " AreaName="..param..
  16. " StartPos="..minetest.pos_to_string(pos1)..
  17. " EndPos=" ..minetest.pos_to_string(pos2))
  18. local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
  19. if not canAdd then
  20. return false, S("You can't protect that area: @1", errMsg)
  21. end
  22. local id = areas:add(name, param, pos1, pos2, nil)
  23. areas:save()
  24. return true, S("Area protected. ID: @1", id)
  25. end
  26. })
  27. minetest.register_chatcommand("set_owner", {
  28. params = S("<PlayerName>").." "..S("<AreaName>"),
  29. description = S("Protect an area between two positions and give"
  30. .." a player access to it without setting the parent of the"
  31. .." area to any existing area"),
  32. privs = areas.adminPrivs,
  33. func = function(name, param)
  34. local ownerName, areaName = param:match('^(%S+)%s(.+)$')
  35. if not ownerName then
  36. return false, S("Invalid usage, see /help @1.", "set_owner")
  37. end
  38. local pos1, pos2 = areas:getPos(name)
  39. if not (pos1 and pos2) then
  40. return false, S("You need to select an area first.")
  41. end
  42. if not areas:player_exists(ownerName) then
  43. return false, S("The player \"@1\" does not exist.", ownerName)
  44. end
  45. minetest.log("action", name.." runs /set_owner. Owner = "..ownerName..
  46. " AreaName = "..areaName..
  47. " StartPos = "..minetest.pos_to_string(pos1)..
  48. " EndPos = " ..minetest.pos_to_string(pos2))
  49. local id = areas:add(ownerName, areaName, pos1, pos2, nil)
  50. areas:save()
  51. minetest.chat_send_player(ownerName,
  52. S("You have been granted control over area #@1. "..
  53. "Type /list_areas to show your areas.", id))
  54. return true, S("Area protected. ID: @1", id)
  55. end
  56. })
  57. minetest.register_chatcommand("add_owner", {
  58. params = S("<ParentID>").." "..S("<PlayerName>").." "..S("<AreaName>"),
  59. description = S("Give a player access to a sub-area beetween two"
  60. .." positions that have already been protected,"
  61. .." Use set_owner if you don't want the parent to be set."),
  62. func = function(name, param)
  63. local pid, ownerName, areaName = param:match('^(%d+) ([^ ]+) (.+)$')
  64. if not pid then
  65. minetest.chat_send_player(name, S("Invalid usage, see /help @1.", "add_owner"))
  66. return
  67. end
  68. local pos1, pos2 = areas:getPos(name)
  69. if not (pos1 and pos2) then
  70. return false, S("You need to select an area first.")
  71. end
  72. if not areas:player_exists(ownerName) then
  73. return false, S("The player \"@1\" does not exist.", ownerName)
  74. end
  75. minetest.log("action", name.." runs /add_owner. Owner = "..ownerName..
  76. " AreaName = "..areaName.." ParentID = "..pid..
  77. " StartPos = "..pos1.x..","..pos1.y..","..pos1.z..
  78. " EndPos = " ..pos2.x..","..pos2.y..","..pos2.z)
  79. -- Check if this new area is inside an area owned by the player
  80. pid = tonumber(pid)
  81. if (not areas:isAreaOwner(pid, name)) or
  82. (not areas:isSubarea(pos1, pos2, pid)) then
  83. return false, S("You can't protect that area.")
  84. end
  85. local id = areas:add(ownerName, areaName, pos1, pos2, pid)
  86. areas:save()
  87. minetest.chat_send_player(ownerName,
  88. S("You have been granted control over area #@1. "..
  89. "Type /list_areas to show your areas.", id))
  90. return true, S("Area protected. ID: @1", id)
  91. end
  92. })
  93. minetest.register_chatcommand("rename_area", {
  94. params = S("<ID>").." "..S("<newName>"),
  95. description = S("Rename an area that you own"),
  96. func = function(name, param)
  97. local id, newName = param:match("^(%d+)%s(.+)$")
  98. if not id then
  99. return false, S("Invalid usage, see /help @1.", "rename_area")
  100. end
  101. id = tonumber(id)
  102. if not id then
  103. return false, S("That area doesn't exist.")
  104. end
  105. if not areas:isAreaOwner(id, name) then
  106. return true, S("You don't own that area.")
  107. end
  108. areas.areas[id].name = newName
  109. areas:save()
  110. return true, S("Area renamed.")
  111. end
  112. })
  113. minetest.register_chatcommand("find_areas", {
  114. params = "<regexp>",
  115. description = S("Find areas using a Lua regular expression"),
  116. privs = areas.adminPrivs,
  117. func = function(name, param)
  118. if param == "" then
  119. return false, S("A regular expression is required.")
  120. end
  121. -- Check expression for validity
  122. local function testRegExp()
  123. ("Test [1]: Player (0,0,0) (0,0,0)"):find(param)
  124. end
  125. if not pcall(testRegExp) then
  126. return false, S("Invalid regular expression.")
  127. end
  128. local matches = {}
  129. for id, area in pairs(areas.areas) do
  130. local str = areas:toString(id)
  131. if str:find(param) then
  132. table.insert(matches, str)
  133. end
  134. end
  135. if #matches > 0 then
  136. return true, table.concat(matches, "\n")
  137. else
  138. return true, S("No matches found.")
  139. end
  140. end
  141. })
  142. minetest.register_chatcommand("list_areas", {
  143. description = S("List your areas, or all areas if you are an admin."),
  144. func = function(name, param)
  145. local admin = minetest.check_player_privs(name, areas.adminPrivs)
  146. local areaStrings = {}
  147. for id, area in pairs(areas.areas) do
  148. if admin or areas:isAreaOwner(id, name) then
  149. table.insert(areaStrings, areas:toString(id))
  150. end
  151. end
  152. if #areaStrings == 0 then
  153. return true, S("No visible areas.")
  154. end
  155. return true, table.concat(areaStrings, "\n")
  156. end
  157. })
  158. minetest.register_chatcommand("recursive_remove_areas", {
  159. params = S("<ID>"),
  160. description = S("Recursively remove areas using an ID"),
  161. func = function(name, param)
  162. local id = tonumber(param)
  163. if not id then
  164. return false, S("Invalid usage, see"
  165. .." /help @1.", "recursive_remove_areas")
  166. end
  167. if not areas:isAreaOwner(id, name) then
  168. return false, S("Area @1 does not exist or is"
  169. .." not owned by you.", id)
  170. end
  171. areas:remove(id, true)
  172. areas:save()
  173. return true, S("Removed area @1 and it's sub areas.", id)
  174. end
  175. })
  176. minetest.register_chatcommand("remove_area", {
  177. params = S("<ID>"),
  178. description = S("Remove an area using an ID"),
  179. func = function(name, param)
  180. local id = tonumber(param)
  181. if not id then
  182. return false, S("Invalid usage, see /help @1.", "remove_area")
  183. end
  184. if not areas:isAreaOwner(id, name) then
  185. return false, S("Area @1 does not exist or"
  186. .." is not owned by you.", id)
  187. end
  188. areas:remove(id)
  189. areas:save()
  190. return true, S("Removed area @1", id)
  191. end
  192. })
  193. minetest.register_chatcommand("change_owner", {
  194. params = S("<ID>").." "..S("<NewOwner>"),
  195. description = S("Change the owner of an area using its ID"),
  196. func = function(name, param)
  197. local id, newOwner = param:match("^(%d+)%s(%S+)$")
  198. if not id then
  199. return false, S("Invalid usage, see"
  200. .." /help @1.", "change_owner")
  201. end
  202. if not areas:player_exists(newOwner) then
  203. return false, S("The player \"@1\" does not exist.", newOwner)
  204. end
  205. id = tonumber(id)
  206. if not areas:isAreaOwner(id, name) then
  207. return false, S("Area @1 does not exist"
  208. .." or is not owned by you.", id)
  209. end
  210. areas.areas[id].owner = newOwner
  211. areas:save()
  212. minetest.chat_send_player(newOwner,
  213. S("@1 has given you control over the area \"@2\" (ID @3).",
  214. name, areas.areas[id].name, id))
  215. return true, S("Owner changed.")
  216. end
  217. })
  218. minetest.register_chatcommand("area_open", {
  219. params = S("<ID>"),
  220. description = S("Toggle an area open (anyone can interact) or closed"),
  221. func = function(name, param)
  222. local id = tonumber(param)
  223. if not id then
  224. return false, S("Invalid usage, see /help @1.", "area_open")
  225. end
  226. if not areas:isAreaOwner(id, name) then
  227. return false, S("Area @1 does not exist"
  228. .." or is not owned by you.", id)
  229. end
  230. local open = not areas.areas[id].open
  231. -- Save false as nil to avoid inflating the DB.
  232. areas.areas[id].open = open or nil
  233. areas:save()
  234. return true, open and S("Area opened.") or S("Area closed.")
  235. end
  236. })
  237. if areas.factions_available then
  238. minetest.register_chatcommand("area_faction_open", {
  239. params = S("<ID>"),
  240. description = S("Toggle an area open/closed for members in your faction."),
  241. func = function(name, param)
  242. local id = tonumber(param)
  243. if not id then
  244. return false, S("Invalid usage, see /help @1.", "area_faction_open")
  245. end
  246. if not areas:isAreaOwner(id, name) then
  247. return false, S("Area @1 does not exist"
  248. .." or is not owned by you.", id)
  249. end
  250. local open = not areas.areas[id].faction_open
  251. -- Save false as nil to avoid inflating the DB.
  252. areas.areas[id].faction_open = open or nil
  253. areas:save()
  254. return true, open and S("Area opened for faction members.")
  255. or S("Area closed for faction members.")
  256. end
  257. })
  258. end
  259. minetest.register_chatcommand("move_area", {
  260. params = S("<ID>"),
  261. description = S("Move (or resize) an area to the current positions."),
  262. privs = areas.adminPrivs,
  263. func = function(name, param)
  264. local id = tonumber(param)
  265. if not id then
  266. return false, S("Invalid usage, see /help @1.", "move_area")
  267. end
  268. local area = areas.areas[id]
  269. if not area then
  270. return false, S("Area does not exist.")
  271. end
  272. local pos1, pos2 = areas:getPos(name)
  273. if not pos1 then
  274. return false, S("You need to select an area first.")
  275. end
  276. areas:move(id, area, pos1, pos2)
  277. areas:save()
  278. return true, S("Area successfully moved.")
  279. end,
  280. })
  281. minetest.register_chatcommand("area_info", {
  282. description = S("Get information about area configuration and usage."),
  283. func = function(name, param)
  284. local lines = {}
  285. local privs = minetest.get_player_privs(name)
  286. -- Short (and fast to access) names
  287. local cfg = areas.config
  288. local self_prot = cfg.self_protection
  289. local prot_priv = cfg.self_protection_privilege
  290. local limit = cfg.self_protection_max_areas
  291. local limit_high = cfg.self_protection_max_areas_high
  292. local size_limit = cfg.self_protection_max_size
  293. local size_limit_high = cfg.self_protection_max_size_high
  294. local has_high_limit = privs.areas_high_limit
  295. local has_prot_priv = not prot_priv or privs[prot_priv]
  296. local can_prot = privs.areas or (self_prot and has_prot_priv)
  297. local max_count = can_prot and
  298. (has_high_limit and limit_high or limit) or 0
  299. local max_size = has_high_limit and
  300. size_limit_high or size_limit
  301. -- Self protection information
  302. local self_prot_line = self_prot and S("Self protection is enabled.") or
  303. S("Self protection is disabled.")
  304. table.insert(lines, self_prot_line)
  305. -- Privilege information
  306. local priv_line = has_prot_priv and
  307. S("You have the necessary privilege (\"@1\").", prot_priv) or
  308. S("You don't have the necessary privilege (\"@1\").", prot_priv)
  309. table.insert(lines, priv_line)
  310. if privs.areas then
  311. table.insert(lines, S("You are an area"..
  312. " administrator (\"areas\" privilege)."))
  313. elseif has_high_limit then
  314. table.insert(lines,
  315. S("You have extended area protection"..
  316. " limits (\"areas_high_limit\" privilege)."))
  317. end
  318. -- Area count
  319. local area_num = 0
  320. for id, area in pairs(areas.areas) do
  321. if area.owner == name then
  322. area_num = area_num + 1
  323. end
  324. end
  325. table.insert(lines, S("You have @1 areas.", area_num))
  326. -- Area limit
  327. local area_limit_line = privs.areas and
  328. S("Limit: no area count limit") or
  329. S("Limit: @1 areas", max_count)
  330. table.insert(lines, area_limit_line)
  331. -- Area size limits
  332. local function size_info(str, size)
  333. table.insert(lines, S("@1 spanning up to @2x@3x@4.",
  334. str, size.x, size.y, size.z))
  335. end
  336. local function priv_limit_info(lpriv, lmax_count, lmax_size)
  337. size_info(S("Players with the \"@1\" privilege"..
  338. " can protect up to @2 areas", lpriv, lmax_count),
  339. lmax_size)
  340. end
  341. if self_prot then
  342. if privs.areas then
  343. priv_limit_info(prot_priv,
  344. limit, size_limit)
  345. priv_limit_info("areas_high_limit",
  346. limit_high, size_limit_high)
  347. elseif has_prot_priv then
  348. size_info(S("You can protect areas"), max_size)
  349. end
  350. end
  351. return true, table.concat(lines, "\n")
  352. end,
  353. })
  354. minetest.register_chatcommand("areas_cleanup", {
  355. description = S("Removes all ownerless areas"),
  356. privs = areas.adminPrivs,
  357. func = function()
  358. local total, count = 0, 0
  359. local aareas = areas.areas
  360. for id, _ in pairs(aareas) do
  361. local owner = aareas[id].owner
  362. if not areas:player_exists(owner) then
  363. areas:remove(id)
  364. count = count + 1
  365. end
  366. total = total + 1
  367. end
  368. areas:save()
  369. return true, "Total areas: " .. total .. ", Removed " ..
  370. count .. " areas. New count: " .. (total - count)
  371. end
  372. })