chatcommands.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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 = {creative=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. minetest.register_chatcommand("move_area", {
  238. params = S("<ID>"),
  239. description = S("Move (or resize) an area to the current positions."),
  240. privs = areas.adminPrivs,
  241. func = function(name, param)
  242. local id = tonumber(param)
  243. if not id then
  244. return false, S("Invalid usage, see /help @1.", "move_area")
  245. end
  246. local area = areas.areas[id]
  247. if not area then
  248. return false, S("Area does not exist.")
  249. end
  250. local pos1, pos2 = areas:getPos(name)
  251. if not pos1 then
  252. return false, S("You need to select an area first.")
  253. end
  254. areas:move(id, area, pos1, pos2)
  255. areas:save()
  256. return true, S("Area successfully moved.")
  257. end,
  258. })
  259. minetest.register_chatcommand("area_info", {
  260. description = S("Get information about area configuration and usage."),
  261. func = function(name, param)
  262. local lines = {}
  263. local privs = minetest.get_player_privs(name)
  264. -- Short (and fast to access) names
  265. local cfg = areas.config
  266. local self_prot = cfg.self_protection
  267. local prot_priv = cfg.self_protection_privilege
  268. local limit = cfg.self_protection_max_areas
  269. local limit_high = cfg.self_protection_max_areas_high
  270. local size_limit = cfg.self_protection_max_size
  271. local size_limit_high = cfg.self_protection_max_size_high
  272. local has_high_limit = privs.areas_high_limit
  273. local has_prot_priv = not prot_priv or privs[prot_priv]
  274. local can_prot = privs.creative or (self_prot and has_prot_priv)
  275. local max_count = can_prot and
  276. (has_high_limit and limit_high or limit) or 0
  277. local max_size = has_high_limit and
  278. size_limit_high or size_limit
  279. -- Self protection information
  280. local self_prot_line = self_prot and S("Self protection is enabled.") or
  281. S("Self protection is disabled.")
  282. table.insert(lines, self_prot_line)
  283. -- Privilege information
  284. local priv_line = has_prot_priv and
  285. S("You have the necessary privilege (\"@1\").", prot_priv) or
  286. S("You don't have the necessary privilege (\"@1\").", prot_priv)
  287. table.insert(lines, priv_line)
  288. if privs.areas then
  289. table.insert(lines, S("You are an area"..
  290. " administrator (\"areas\" privilege)."))
  291. elseif has_high_limit then
  292. table.insert(lines,
  293. S("You have extended area protection"..
  294. " limits (\"areas_high_limit\" privilege)."))
  295. end
  296. -- Area count
  297. local area_num = 0
  298. for id, area in pairs(areas.areas) do
  299. if area.owner == name then
  300. area_num = area_num + 1
  301. end
  302. end
  303. table.insert(lines, S("You have @1 areas.", area_num))
  304. -- Area limit
  305. local area_limit_line = privs.areas and
  306. S("Limit: no area count limit") or
  307. S("Limit: @1 areas", max_count)
  308. table.insert(lines, area_limit_line)
  309. -- Area size limits
  310. local function size_info(str, size)
  311. table.insert(lines, S("@1 spanning up to @2x@3x@4.",
  312. str, size.x, size.y, size.z))
  313. end
  314. local function priv_limit_info(lpriv, lmax_count, lmax_size)
  315. size_info(S("Players with the \"@1\" privilege"..
  316. " can protect up to @2 areas", lpriv, lmax_count),
  317. lmax_size)
  318. end
  319. if self_prot then
  320. if privs.areas then
  321. priv_limit_info(prot_priv,
  322. limit, size_limit)
  323. priv_limit_info("areas_high_limit",
  324. limit_high, size_limit_high)
  325. elseif has_prot_priv then
  326. size_info(S("You can protect areas"), max_size)
  327. end
  328. end
  329. return true, table.concat(lines, "\n")
  330. end,
  331. })
  332. minetest.register_chatcommand("areas_cleanup", {
  333. description = S("Removes all ownerless areas"),
  334. privs = areas.adminPrivs,
  335. func = function()
  336. local total, count = 0, 0
  337. local aareas = areas.areas
  338. for id, _ in pairs(aareas) do
  339. local owner = aareas[id].owner
  340. if not areas:player_exists(owner) then
  341. areas:remove(id)
  342. count = count + 1
  343. end
  344. total = total + 1
  345. end
  346. areas:save()
  347. return true, "Total areas: " .. total .. ", Removed " ..
  348. count .. " areas. New count: " .. (total - count)
  349. end
  350. })