Add support for the AreaStore API

This commit is contained in:
est31 2015-07-09 11:42:13 +02:00 committed by ShadowNinja
parent 177f659f4e
commit 8b0b8c1ad2
2 changed files with 139 additions and 41 deletions

119
api.lua
View File

@ -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) function areas:getAreasAtPos(pos)
local a = {} local res = {}
local px, py, pz = pos.x, pos.y, pos.z if self.store then
for id, area in pairs(self.areas) do local a = self.store:get_areas_for_pos(pos, false, true)
local ap1, ap2 = area.pos1, area.pos2 for store_id, store_area in pairs(a) do
if px >= ap1.x and px <= ap2.x and local id = tonumber(store_area.data)
py >= ap1.y and py <= ap2.y and res[id] = self.areas[id]
pz >= ap1.z and pz <= ap2.z then end
a[id] = area 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
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 end
-- Checks if the area is unprotected or owned by you -- 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 -- 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 -- owned by the player, but with multiple protection zones, none of which
-- cover the entire checked area. -- cover the entire checked area.
-- @param name (optional) player name. If not specified checks for any intersecting areas. -- @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 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 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) function areas:canInteractInArea(pos1, pos2, name, allow_open)
if name and minetest.check_player_privs(name, self.adminPrivs) then if name and minetest.check_player_privs(name, self.adminPrivs) then
return true return true
end end
areas:sortPos(pos1, pos2) self:sortPos(pos1, pos2)
-- First check for a fully enclosing owned area.
if name then -- Intersecting non-owned area ID, if found.
for id, area in pairs(self.areas) do local blocking_area = nil
-- A little optimization: isAreaOwner isn't necessary
-- here since we're iterating through all areas. local areas = self:getAreasIntersectingArea(pos1, pos2)
if area.owner == name and for id, area in pairs(areas) do
self:isSubarea(pos1, pos2, id) then -- First check for a fully enclosing owned area.
return true -- A little optimization: isAreaOwner isn't necessary
end -- here since we're iterating over all relevant areas.
if area.owner == name and
self:isSubarea(pos1, pos2, id) then
return true
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
end end
-- Then check for intersecting (non-owned) areas.
for id, area in pairs(self.areas) do if blocking_area then
local p1, p2 = area.pos1, area.pos2 return false, blocking_area
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
end
end end
-- There are no intersecting areas or they are only partially
-- intersecting areas and they are all owned by the player.
return true return true
end end

View File

@ -30,6 +30,40 @@ function areas:load()
self.areas = {} self.areas = {}
end end
file:close() 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 end
-- Finds the first usable index in a table -- Finds the first usable index in a table
@ -41,15 +75,28 @@ local function findFirstUnusedIndex(t)
return i return i
end 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) function areas:add(owner, name, pos1, pos2, parent)
local id = findFirstUnusedIndex(self.areas) local id = findFirstUnusedIndex(self.areas)
self.areas[id] = {name=name, pos1=pos1, pos2=pos2, owner=owner, self.areas[id] = {
parent=parent} 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 return id
end 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 -- If a area is deleted non-recursively the children will
-- have the removed area's parent as their new parent. -- have the removed area's parent as their new parent.
function areas:remove(id, recurse) function areas:remove(id, recurse)
@ -73,6 +120,12 @@ function areas:remove(id, recurse)
-- Remove main entry -- Remove main entry
self.areas[id] = nil 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 end
-- Checks if a area between two points is entirely contained by another area -- Checks if a area between two points is entirely contained by another area