summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorest31 <MTest31@outlook.com>2015-07-09 11:42:13 +0200
committerShadowNinja <shadowninja@minetest.net>2015-10-29 16:49:35 -0400
commit8b0b8c1ad26afdae906e9fd6332218d79a33bd1d (patch)
tree889d44cf4a47d3262d6ac1693c9e5b597390d5ee
parent177f659f4e6c9ea3ddf12f416fc6afb8ae66648d (diff)
Add support for the AreaStore API
-rw-r--r--api.lua119
-rw-r--r--internal.lua61
2 files changed, 139 insertions, 41 deletions
diff --git a/api.lua b/api.lua
index 1a4026b..e8b9c7a 100644
--- a/api.lua
+++ b/api.lua
@@ -1,17 +1,54 @@
--- Returns a list of areas that include the provided position
+--- Returns a list of areas that include the provided position.
function areas:getAreasAtPos(pos)
- local a = {}
- local px, py, pz = pos.x, pos.y, pos.z
- for id, area in pairs(self.areas) do
- local ap1, ap2 = area.pos1, area.pos2
- if px >= ap1.x and px <= ap2.x and
- py >= ap1.y and py <= ap2.y and
- pz >= ap1.z and pz <= ap2.z then
- a[id] = area
+ local res = {}
+ if self.store then
+ local a = self.store:get_areas_for_pos(pos, false, true)
+ for store_id, store_area in pairs(a) do
+ local id = tonumber(store_area.data)
+ res[id] = self.areas[id]
+ end
+ else
+ local px, py, pz = pos.x, pos.y, pos.z
+ for id, area in pairs(self.areas) do
+ local ap1, ap2 = area.pos1, area.pos2
+ if
+ (px >= ap1.x and px <= ap2.x) and
+ (py >= ap1.y and py <= ap2.y) and
+ (pz >= ap1.z and pz <= ap2.z) then
+ res[id] = area
+ end
end
end
- return a
+ return res
+end
+
+--- Returns areas that intersect with the passed area.
+function areas:getAreasIntersectingArea(pos1, pos2)
+ local res = {}
+ if self.store then
+ local a = self.store:get_areas_in_area(pos1, pos2,
+ true, false, true)
+ for store_id, store_area in pairs(a) do
+ local id = tonumber(store_area.data)
+ res[id] = self.areas[id]
+ end
+ else
+ self:sortPos(pos1, pos2)
+ local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z
+ local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z
+ for id, area in pairs(self.areas) do
+ local ap1, ap2 = area.pos1, area.pos2
+ if
+ (ap1.x <= p2x and ap2.x >= p1x) and
+ (ap1.y <= p2y and ap2.y >= p1y) and
+ (ap1.z <= p2z and ap2.z >= p1z) then
+ -- Found an intersecting area.
+ res[id] = area
+ end
+ end
+ end
+ return res
end
-- Checks if the area is unprotected or owned by you
@@ -43,41 +80,49 @@ end
-- Note that this fails and returns false when the specified area is fully
-- owned by the player, but with multiple protection zones, none of which
-- cover the entire checked area.
--- @param name (optional) player name. If not specified checks for any intersecting areas.
--- @param allow_open Whether open areas should be counted as is they didn't exist.
+-- @param name (optional) Player name. If not specified checks for any intersecting areas.
+-- @param allow_open Whether open areas should be counted as if they didn't exist.
-- @return Boolean indicating whether the player can interact in that area.
--- @return Un-owned intersecting area id, if found.
+-- @return Un-owned intersecting area ID, if found.
function areas:canInteractInArea(pos1, pos2, name, allow_open)
if name and minetest.check_player_privs(name, self.adminPrivs) then
return true
end
- areas:sortPos(pos1, pos2)
- -- First check for a fully enclosing owned area.
- if name then
- for id, area in pairs(self.areas) do
- -- A little optimization: isAreaOwner isn't necessary
- -- here since we're iterating through all areas.
- if area.owner == name and
- self:isSubarea(pos1, pos2, id) then
- return true
- end
+ self:sortPos(pos1, pos2)
+
+ -- Intersecting non-owned area ID, if found.
+ local blocking_area = nil
+
+ local areas = self:getAreasIntersectingArea(pos1, pos2)
+ for id, area in pairs(areas) do
+ -- First check for a fully enclosing owned area.
+ -- A little optimization: isAreaOwner isn't necessary
+ -- here since we're iterating over all relevant areas.
+ if area.owner == name and
+ self:isSubarea(pos1, pos2, id) then
+ return true
end
- end
- -- Then check for intersecting (non-owned) areas.
- for id, area in pairs(self.areas) do
- local p1, p2 = area.pos1, area.pos2
- if (p1.x <= pos2.x and p2.x >= pos1.x) and
- (p1.y <= pos2.y and p2.y >= pos1.y) and
- (p1.z <= pos2.z and p2.z >= pos1.z) then
- -- Found an intersecting area.
- -- Return if the area is closed or open areas aren't
- -- allowed, and the area isn't owned.
- if (not allow_open or not area.open) and
- (not name or not areas:isAreaOwner(id, name)) then
- return false, id
- end
+
+ -- Then check for intersecting non-owned (blocking) areas.
+ -- We don't bother with this check if we've already found a
+ -- blocking area, as the check is somewhat expensive.
+ -- The area blocks if the area is closed or open areas aren't
+ -- acceptable to the caller, and the area isn't owned.
+ -- Note: We can't return directly here, because there might be
+ -- an exclosing owned area that we haven't gotten to yet.
+ if not blocking_area and
+ (not allow_open or not area.open) and
+ (not name or not self:isAreaOwner(id, name)) then
+ blocking_area = id
end
end
+
+ if blocking_area then
+ return false, blocking_area
+ end
+
+ -- There are no intersecting areas or they are only partially
+ -- intersecting areas and they are all owned by the player.
return true
end
diff --git a/internal.lua b/internal.lua
index f1b048d..ea94a27 100644
--- a/internal.lua
+++ b/internal.lua
@@ -30,6 +30,40 @@ function areas:load()
self.areas = {}
end
file:close()
+ self:populateStore()
+end
+
+--- Checks an AreaStore ID.
+-- Deletes the AreaStore (falling back to the iterative method)
+-- and prints an error message if the ID is invalid.
+-- @return Whether the ID was valid.
+function areas:checkAreaStoreId(sid)
+ if not sid then
+ minetest.log("error", "AreaStore failed to find an ID for an "
+ .."area! Falling back to iterative area checking.")
+ self.store = nil
+ self.store_ids = nil
+ end
+ return sid and true or false
+end
+
+-- Populates the AreaStore after loading, if needed.
+function areas:populateStore()
+ if not rawget(_G, "AreaStore") then
+ return
+ end
+ local store = AreaStore()
+ local store_ids = {}
+ for id, area in pairs(areas.areas) do
+ local sid = store:insert_area(area.pos1,
+ area.pos2, tostring(id))
+ if not self:checkAreaStoreId(sid) then
+ return
+ end
+ store_ids[id] = sid
+ end
+ self.store = store
+ self.store_ids = store_ids
end
-- Finds the first usable index in a table
@@ -41,15 +75,28 @@ local function findFirstUnusedIndex(t)
return i
end
--- Add a area, returning the new area's id.
+--- Add a area.
+-- @return The new area's ID.
function areas:add(owner, name, pos1, pos2, parent)
local id = findFirstUnusedIndex(self.areas)
- self.areas[id] = {name=name, pos1=pos1, pos2=pos2, owner=owner,
- parent=parent}
+ self.areas[id] = {
+ name = name,
+ pos1 = pos1,
+ pos2 = pos2,
+ owner = owner,
+ parent = parent
+ }
+ -- Add to AreaStore
+ if self.store then
+ local sid = self.store:insert_area(pos1, pos2, tostring(id))
+ if self:checkAreaStoreId(sid) then
+ self.store_ids[id] = sid
+ end
+ end
return id
end
--- Remove a area, and optionally it's children recursively.
+--- Remove a area, and optionally it's children recursively.
-- If a area is deleted non-recursively the children will
-- have the removed area's parent as their new parent.
function areas:remove(id, recurse)
@@ -73,6 +120,12 @@ function areas:remove(id, recurse)
-- Remove main entry
self.areas[id] = nil
+
+ -- Remove from AreaStore
+ if self.store then
+ self.store:remove_area(self.store_ids[id])
+ self.store_ids[id] = nil
+ end
end
-- Checks if a area between two points is entirely contained by another area