internal.lua 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. local S = minetest.get_translator("areas")
  2. function areas:player_exists(name)
  3. return minetest.get_auth_handler().get_auth(name) ~= nil
  4. end
  5. local safe_file_write = minetest.safe_file_write
  6. if safe_file_write == nil then
  7. function safe_file_write(path, content)
  8. local file, err = io.open(path, "w")
  9. if err then
  10. return err
  11. end
  12. file:write(content)
  13. file:close()
  14. end
  15. end
  16. -- Save the areas table to a file
  17. function areas:save()
  18. local datastr = minetest.write_json(self.areas)
  19. if not datastr then
  20. minetest.log("error", "[areas] Failed to serialize area data!")
  21. return
  22. end
  23. return safe_file_write(self.config.filename, datastr)
  24. end
  25. -- Load the areas table from the save file
  26. function areas:load()
  27. local file, err = io.open(self.config.filename, "r")
  28. if err then
  29. self.areas = self.areas or {}
  30. return err
  31. end
  32. local data = file:read("*a")
  33. if data:sub(1, 1) == "[" then
  34. self.areas, err = minetest.parse_json(data)
  35. else
  36. self.areas, err = minetest.deserialize(data)
  37. end
  38. if type(self.areas) ~= "table" then
  39. self.areas = {}
  40. end
  41. if err and #data > 10 then
  42. minetest.log("error", "[areas] Failed to load area data: " ..
  43. tostring(err))
  44. end
  45. file:close()
  46. self:populateStore()
  47. end
  48. --- Checks an AreaStore ID.
  49. -- Deletes the AreaStore (falling back to the iterative method)
  50. -- and prints an error message if the ID is invalid.
  51. -- @return Whether the ID was valid.
  52. function areas:checkAreaStoreId(sid)
  53. if not sid then
  54. minetest.log("error", "AreaStore failed to find an ID for an "
  55. .."area! Falling back to iterative area checking.")
  56. self.store = nil
  57. self.store_ids = nil
  58. end
  59. return sid and true or false
  60. end
  61. -- Populates the AreaStore after loading, if needed.
  62. function areas:populateStore()
  63. if not rawget(_G, "AreaStore") then
  64. return
  65. end
  66. local store = AreaStore()
  67. local store_ids = {}
  68. for id, area in pairs(areas.areas) do
  69. local sid = store:insert_area(area.pos1,
  70. area.pos2, tostring(id))
  71. if not self:checkAreaStoreId(sid) then
  72. return
  73. end
  74. store_ids[id] = sid
  75. end
  76. self.store = store
  77. self.store_ids = store_ids
  78. end
  79. -- Finds the first usable index in a table
  80. -- Eg: {[1]=false,[4]=true} -> 2
  81. local function findFirstUnusedIndex(t)
  82. local i = 0
  83. repeat i = i + 1
  84. until t[i] == nil
  85. return i
  86. end
  87. --- Add a area.
  88. -- @return The new area's ID.
  89. function areas:add(owner, name, pos1, pos2, parent)
  90. local id = findFirstUnusedIndex(self.areas)
  91. self.areas[id] = {
  92. name = name,
  93. pos1 = pos1,
  94. pos2 = pos2,
  95. owner = owner,
  96. parent = parent
  97. }
  98. for i=1, #areas.registered_on_adds do
  99. areas.registered_on_adds[i](id, self.areas[id])
  100. end
  101. -- Add to AreaStore
  102. if self.store then
  103. local sid = self.store:insert_area(pos1, pos2, tostring(id))
  104. if self:checkAreaStoreId(sid) then
  105. self.store_ids[id] = sid
  106. end
  107. end
  108. return id
  109. end
  110. --- Remove a area, and optionally it's children recursively.
  111. -- If a area is deleted non-recursively the children will
  112. -- have the removed area's parent as their new parent.
  113. function areas:remove(id, recurse)
  114. if recurse then
  115. -- Recursively find child entries and remove them
  116. local cids = self:getChildren(id)
  117. for _, cid in pairs(cids) do
  118. self:remove(cid, true)
  119. end
  120. else
  121. -- Update parents
  122. local parent = self.areas[id].parent
  123. local children = self:getChildren(id)
  124. for _, cid in pairs(children) do
  125. -- The subarea parent will be niled out if the
  126. -- removed area does not have a parent
  127. self.areas[cid].parent = parent
  128. end
  129. end
  130. for i=1, #areas.registered_on_removes do
  131. areas.registered_on_removes[i](id)
  132. end
  133. -- Remove main entry
  134. self.areas[id] = nil
  135. -- Remove from AreaStore
  136. if self.store then
  137. self.store:remove_area(self.store_ids[id])
  138. self.store_ids[id] = nil
  139. end
  140. end
  141. --- Move an area.
  142. function areas:move(id, area, pos1, pos2)
  143. area.pos1 = pos1
  144. area.pos2 = pos2
  145. for i=1, #areas.registered_on_moves do
  146. areas.registered_on_moves[i](id, area, pos1, pos2)
  147. end
  148. if self.store then
  149. self.store:remove_area(areas.store_ids[id])
  150. local sid = self.store:insert_area(pos1, pos2, tostring(id))
  151. if self:checkAreaStoreId(sid) then
  152. self.store_ids[id] = sid
  153. end
  154. end
  155. end
  156. -- Checks if a area between two points is entirely contained by another area.
  157. -- Positions must be sorted.
  158. function areas:isSubarea(pos1, pos2, id)
  159. local area = self.areas[id]
  160. if not area then
  161. return false
  162. end
  163. local ap1, ap2 = area.pos1, area.pos2
  164. local ap1x, ap1y, ap1z = ap1.x, ap1.y, ap1.z
  165. local ap2x, ap2y, ap2z = ap2.x, ap2.y, ap2.z
  166. local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z
  167. local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z
  168. if
  169. (p1x >= ap1x and p1x <= ap2x) and
  170. (p2x >= ap1x and p2x <= ap2x) and
  171. (p1y >= ap1y and p1y <= ap2y) and
  172. (p2y >= ap1y and p2y <= ap2y) and
  173. (p1z >= ap1z and p1z <= ap2z) and
  174. (p2z >= ap1z and p2z <= ap2z) then
  175. return true
  176. end
  177. end
  178. -- Returns a table (list) of children of an area given it's identifier
  179. function areas:getChildren(id)
  180. local children = {}
  181. for cid, area in pairs(self.areas) do
  182. if area.parent and area.parent == id then
  183. table.insert(children, cid)
  184. end
  185. end
  186. return children
  187. end
  188. -- Checks if the user has sufficient privileges.
  189. -- If the player is not a administrator it also checks
  190. -- if the area intersects other areas that they do not own.
  191. -- Also checks the size of the area and if the user already
  192. -- has more than max_areas.
  193. function areas:canPlayerAddArea(pos1, pos2, name)
  194. local privs = minetest.get_player_privs(name)
  195. if privs.creative then
  196. return true
  197. end
  198. -- Check self protection privilege, if it is enabled,
  199. -- and if the area is too big.
  200. if not self.config.self_protection or
  201. not privs[areas.config.self_protection_privilege] then
  202. return false, S("Self protection is disabled or you do not have"
  203. .." the necessary privilege.")
  204. end
  205. local max_size = privs.areas_high_limit and
  206. self.config.self_protection_max_size_high or
  207. self.config.self_protection_max_size
  208. if
  209. (pos2.x - pos1.x) > max_size.x or
  210. (pos2.y - pos1.y) > max_size.y or
  211. (pos2.z - pos1.z) > max_size.z then
  212. return false, S("Area is too big.")
  213. end
  214. -- Check number of areas the user has and make sure it not above the max
  215. local count = 0
  216. for _, area in pairs(self.areas) do
  217. if area.owner == name then
  218. count = count + 1
  219. end
  220. end
  221. local max_areas = privs.areas_high_limit and
  222. self.config.self_protection_max_areas_high or
  223. self.config.self_protection_max_areas
  224. if count >= max_areas then
  225. return false, S("You have reached the maximum amount of"
  226. .." areas that you are allowed to protect.")
  227. end
  228. -- Check intersecting areas
  229. local can, id = self:canInteractInArea(pos1, pos2, name)
  230. if not can then
  231. local area = self.areas[id]
  232. return false, S("The area intersects with @1 [@2] (@3).",
  233. area.name, id, area.owner)
  234. end
  235. return true
  236. end
  237. -- Given a id returns a string in the format:
  238. -- "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children"
  239. function areas:toString(id)
  240. local area = self.areas[id]
  241. local message = ("%s [%d]: %s %s %s"):format(
  242. area.name, id, area.owner,
  243. minetest.pos_to_string(area.pos1),
  244. minetest.pos_to_string(area.pos2))
  245. local children = areas:getChildren(id)
  246. if #children > 0 then
  247. message = message.." -> "..table.concat(children, ", ")
  248. end
  249. return message
  250. end
  251. -- Re-order areas in table by their identifiers
  252. function areas:sort()
  253. local sa = {}
  254. for k, area in pairs(self.areas) do
  255. if not area.parent then
  256. table.insert(sa, area)
  257. local newid = #sa
  258. for _, subarea in pairs(self.areas) do
  259. if subarea.parent == k then
  260. subarea.parent = newid
  261. table.insert(sa, subarea)
  262. end
  263. end
  264. end
  265. end
  266. self.areas = sa
  267. end
  268. -- Checks if a player owns an area or a parent of it
  269. function areas:isAreaOwner(id, name)
  270. local cur = self.areas[id]
  271. if cur and minetest.check_player_privs(name, self.adminPrivs) then
  272. return true
  273. end
  274. while cur do
  275. if cur.owner == name then
  276. return true
  277. elseif cur.parent then
  278. cur = self.areas[cur.parent]
  279. else
  280. return false
  281. end
  282. end
  283. return false
  284. end