forked from nalc/areas
Initial commit
This commit is contained in:
commit
4ae050a3ae
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*~
|
||||
|
51
api.lua
Normal file
51
api.lua
Normal file
@ -0,0 +1,51 @@
|
||||
--TODO Less code duplication
|
||||
|
||||
-- Checks if the area is unprotected or owned by you
|
||||
function areas:canInteract(pos, name)
|
||||
if minetest.check_player_privs(name, {areas=true}) then
|
||||
return true
|
||||
end
|
||||
local owned = false
|
||||
for _, area in pairs(self.areas) do
|
||||
p1, p2 = area.pos1, area.pos2
|
||||
if pos.x >= p1.x and pos.x <= p2.x and
|
||||
pos.y >= p1.y and pos.y <= p2.y and
|
||||
pos.z >= p1.z and pos.z <= p2.z then
|
||||
if area.owner == name then
|
||||
return true
|
||||
else
|
||||
owned = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return not owned
|
||||
end
|
||||
|
||||
-- Returns a table (list) of all players that own an area
|
||||
function areas:getNodeOwners(pos)
|
||||
local owners = {}
|
||||
for _, area in pairs(self.areas) do
|
||||
if pos.x >= area.pos1.x and pos.x <= area.pos2.x and
|
||||
pos.y >= area.pos1.y and pos.y <= area.pos2.y and
|
||||
pos.z >= area.pos1.z and pos.z <= area.pos2.z then
|
||||
if area.owner ~= nil then
|
||||
table.insert(owners, area.owner)
|
||||
end
|
||||
end
|
||||
end
|
||||
return owners
|
||||
end
|
||||
|
||||
-- Checks if an area has an owner
|
||||
function areas.hasOwner(pos)
|
||||
for _, area in pairs(areas.areas) do
|
||||
p1, p2 = area.pos1, area.pos2
|
||||
if pos.x >= p1.x and pos.x <= p2.x and
|
||||
pos.y >= p1.y and pos.y <= p2.y and
|
||||
pos.z >= p1.z and pos.z <= p2.z then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
302
chatcommands.lua
Normal file
302
chatcommands.lua
Normal file
@ -0,0 +1,302 @@
|
||||
minetest.register_chatcommand("protect", {
|
||||
params = "<AreaName>",
|
||||
description = "Protect your own area",
|
||||
privs = {[areas.self_protection_privilege]=true},
|
||||
func = function(name, param)
|
||||
if param ~= "" then
|
||||
|
||||
local pos1, pos2 = {}, {}
|
||||
if areas:getPos1(name) and areas:getPos2(name) then
|
||||
pos1 = areas:getPos1(name)
|
||||
pos2 = areas:getPos2(name)
|
||||
pos1, pos2 = areas:sortPos(pos1, pos2)
|
||||
else
|
||||
minetest.chat_send_player(name, 'You need to select an area first')
|
||||
return
|
||||
end
|
||||
|
||||
minetest.log("action", "/protect invoked, owner="..name..
|
||||
" areaname="..param..
|
||||
" startpos="..minetest.pos_to_string(pos1)..
|
||||
" endpos=" ..minetest.pos_to_string(pos2))
|
||||
|
||||
local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
|
||||
if not canAdd then
|
||||
minetest.chat_send_player(name, "You can't protect that area: "..errMsg)
|
||||
return
|
||||
end
|
||||
|
||||
areas:add(name, param, pos1, pos2, nil)
|
||||
areas:save()
|
||||
|
||||
minetest.chat_send_player(name, "Area protected")
|
||||
else
|
||||
minetest.chat_send_player(name, 'Invalid usage, see /help protect')
|
||||
end
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("set_owner", {
|
||||
params = "<PlayerName> <AreaName>",
|
||||
description = "Protect an area beetween two positions and give a player access to it without setting the parent of the area to any existing area",
|
||||
privs = {areas=true},
|
||||
func = function(name, param)
|
||||
if param and param ~= "" then
|
||||
local found, _, ownername, areaname = param:find('^([^%s]+)%s(.+)$')
|
||||
|
||||
if not found then
|
||||
minetest.chat_send_player(name, "Incorrect usage, see /help set_owner")
|
||||
return
|
||||
end
|
||||
|
||||
local pos1, pos2 = {}, {}
|
||||
if areas:getPos1(name) and areas:getPos2(name) then
|
||||
pos1 = areas:getPos1(name)
|
||||
pos2 = areas:getPos2(name)
|
||||
pos1, pos2 = areas:sortPos(pos1, pos2)
|
||||
else
|
||||
minetest.chat_send_player(name, 'You need to select an area first')
|
||||
return
|
||||
end
|
||||
|
||||
if not areas:player_exists(ownername) then
|
||||
minetest.chat_send_player(name, 'The player "'..ownername..'" does not exist')
|
||||
return
|
||||
end
|
||||
|
||||
--local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
|
||||
--if not canAdd then
|
||||
-- minetest.chat_send_player(name, "You can't protect that area: "..errMsg)
|
||||
-- return
|
||||
--end
|
||||
|
||||
minetest.log("action", "/set_owner invoked, Owner="..ownername..
|
||||
" AreaName="..areaname..
|
||||
" StartPos="..minetest.pos_to_string(pos1)..
|
||||
" EndPos=" ..minetest.pos_to_string(pos2))
|
||||
|
||||
areas:add(ownername, areaname, pos1, pos2, nil)
|
||||
areas:save()
|
||||
|
||||
minetest.chat_send_player(ownername, "A concession has been granted to you! Type /list_areas to show your concessions.")
|
||||
minetest.chat_send_player(name, "Area protected")
|
||||
else
|
||||
minetest.chat_send_player(name, 'Invalid usage, see /help set_owner')
|
||||
end
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("add_owner", {
|
||||
params = "<ParentID> <Player> <AreaName>",
|
||||
description = "Give a player access to a sub-area beetween two positions that have already been protected, use set_owner if you don't want the parent to be set",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
if param and param ~= "" then
|
||||
local found, _, pid, ownername, areaname = param:find('^(%d+)%s([^%s]+)%s(.+)$')
|
||||
|
||||
if not found then
|
||||
minetest.chat_send_player(name, "Incorrect usage, see /help set_owner")
|
||||
return
|
||||
end
|
||||
|
||||
local pos1, pos2 = {}, {}
|
||||
if areas:getPos1(name) and areas:getPos2(name) then
|
||||
pos1 = areas:getPos1(name)
|
||||
pos2 = areas:getPos2(name)
|
||||
pos1, pos2 = areas:sortPos(pos1, pos2)
|
||||
else
|
||||
minetest.chat_send_player(name, 'You need to select an area first')
|
||||
return
|
||||
end
|
||||
|
||||
if not areas:player_exists(ownername) then
|
||||
minetest.chat_send_player(name, 'The player "'..ownername..'" does not exist')
|
||||
return
|
||||
end
|
||||
|
||||
minetest.log("action", "add_owner invoked, Owner = "..ownername..
|
||||
" AreaName = "..areaname.." ParentID = "..pid..
|
||||
" StartPos = "..pos1.x..","..pos1.y..","..pos1.z..
|
||||
" EndPos = " ..pos2.x..","..pos2.y..","..pos2.z)
|
||||
|
||||
--Look to see if this new area is inside an area owned by the player using this function
|
||||
pid = tonumber(pid)
|
||||
if (not areas:isAreaOwner(pid, name)) or
|
||||
(not areas:isSubarea(pos1, pos2, pid)) then
|
||||
minetest.chat_send_player(name, "You can't protect that area")
|
||||
return
|
||||
end
|
||||
|
||||
areas:add(ownername, areaname, pos1, pos2, pid)
|
||||
areas:save()
|
||||
|
||||
minetest.chat_send_player(ownername, "A concession has been granted to you! Type /list_areas to show your concessions.")
|
||||
minetest.chat_send_player(name, "You granted "..ownername.." a concession successfully!")
|
||||
else
|
||||
minetest.chat_send_player(name, 'Invalid usage, see /help add_owner')
|
||||
end
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("rename_area", {
|
||||
params = "<ID> <newName>",
|
||||
description = "Rename a area that you own",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local found, _, id, newName = param:find("^(%d+)%s(.+)$")
|
||||
|
||||
if not found then
|
||||
minetest.chat_send_player(name, "Invalid usage, see /help rename_area")
|
||||
return
|
||||
end
|
||||
|
||||
index = areas:getIndexById(tonumber(id))
|
||||
|
||||
if not index then
|
||||
minetest.chat_send_player(name, "That area doesn't exist")
|
||||
return
|
||||
end
|
||||
|
||||
if not areas:isAreaOwner(id, name) then
|
||||
minetest.chat_send_player(name, "You don't own that area")
|
||||
return
|
||||
end
|
||||
|
||||
areas.areas[index].name = newName
|
||||
areas:save()
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("list_owners", {
|
||||
params = "",
|
||||
description = "list the players that can edit the area you are in",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local owners = areas:getNodeOwners(vector.round(minetest.get_player_by_name(name):getpos()))
|
||||
if #owners > 0 then
|
||||
minetest.chat_send_player(name, "Owners: "..table.concat(owners, ", "))
|
||||
else
|
||||
minetest.chat_send_player(name, "Your position is unowned")
|
||||
end
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("find_areas", {
|
||||
params = "<regexp>",
|
||||
description = "Find areas using a Lua regular expression",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
if param and param ~= "" then
|
||||
local found = false
|
||||
for _, area in pairs(areas.areas) do
|
||||
if areas:isAreaOwner(area.id, name) and
|
||||
areas:toString(area):find(param) then
|
||||
minetest.chat_send_player(name, areas:toString(area))
|
||||
found = true
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
minetest.chat_send_player(name, "No matches found")
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name, "Regular expression required")
|
||||
end
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("list_areas", {
|
||||
params = "",
|
||||
description = "list the areas you own, or all areas if you have privileges",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
admin = minetest.check_player_privs(name, {areas=true})
|
||||
if admin then
|
||||
minetest.chat_send_player(name, "Showing all owner entries.")
|
||||
else
|
||||
minetest.chat_send_player(name, "Showing your owner entries (You can only modify these).")
|
||||
end
|
||||
for _, area in pairs(areas.areas) do
|
||||
if admin or area.owner == name then
|
||||
minetest.chat_send_player(name, areas:toString(area))
|
||||
end
|
||||
end
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("recursive_remove_areas", {
|
||||
params = "<id>",
|
||||
description = "Recursively remove areas using an id",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local id = tonumber(param)
|
||||
if not id then
|
||||
minetest.chat_send_player(name, 'Invalid usage, see /help recursive_remove_areas')
|
||||
minetest.chat_send_player(name, 'Use /list_areas to see entries')
|
||||
return
|
||||
end
|
||||
|
||||
if areas:isAreaOwner(id, name) then
|
||||
areas:remove(id, true)
|
||||
areas:sort()
|
||||
areas:save()
|
||||
else
|
||||
minetest.chat_send_player(name, "Area "..id.." does not exist or is not owned by you")
|
||||
return
|
||||
end
|
||||
minetest.chat_send_player(name, 'Removed area '..id..'and sub areas')
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("remove_area", {
|
||||
params = "<id>",
|
||||
description = "Remove an area using an id",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local id = tonumber(param)
|
||||
if not id then
|
||||
minetest.chat_send_player(name, 'Invalid usage, see /help remove_area')
|
||||
minetest.chat_send_player(name, 'Use /list_areas to see entries')
|
||||
return
|
||||
end
|
||||
|
||||
if areas:isAreaOwner(id, name) then
|
||||
areas:remove(id, false)
|
||||
areas:sort()
|
||||
areas:save()
|
||||
else
|
||||
minetest.chat_send_player(name, "Area "..id.." does not exist or is not owned by you")
|
||||
return
|
||||
end
|
||||
minetest.chat_send_player(name, 'Removed area '..id)
|
||||
end})
|
||||
|
||||
|
||||
minetest.register_chatcommand("change_owner", {
|
||||
params = "<id> <newplayer>",
|
||||
description = "change the owner of an area using its id",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local found, _, id, new_owner = param:find('^(%d+)%s+([^%s]+)$')
|
||||
|
||||
if not found then
|
||||
minetest.chat_send_player(name, 'Invalid usage, see /help change_area_owner')
|
||||
minetest.chat_send_player(name, 'Use /list_areas to see entries')
|
||||
return
|
||||
end
|
||||
|
||||
if not areas:player_exists(new_owner) then
|
||||
minetest.chat_send_player(name, 'The player "'..new_owner..'" does not exist')
|
||||
return
|
||||
end
|
||||
|
||||
id = tonumber(id)
|
||||
if areas:isAreaOwner(id, name) then
|
||||
areas.areas[areas:getIndexById(id)].owner = new_owner
|
||||
areas:save()
|
||||
minetest.chat_send_player(name, 'Owner changed succesfully')
|
||||
minetest.chat_send_player(new_owner, name..'" has granted you a concession!')
|
||||
else
|
||||
minetest.chat_send_player(new_owner, "Area "..id.." does not exist or is not owned by you")
|
||||
end
|
||||
end})
|
||||
|
0
depends.txt
Normal file
0
depends.txt
Normal file
31
init.lua
Normal file
31
init.lua
Normal file
@ -0,0 +1,31 @@
|
||||
-- Areas mod by ShadowNinja
|
||||
-- Based on node_ownership
|
||||
-- License: GPLv2+
|
||||
|
||||
areas = {}
|
||||
|
||||
areas.startTime = os.clock()
|
||||
|
||||
areas.modpath = minetest.get_modpath("areas")
|
||||
dofile(areas.modpath.."/settings.lua")
|
||||
dofile(areas.modpath.."/api.lua")
|
||||
dofile(areas.modpath.."/internal.lua")
|
||||
dofile(areas.modpath.."/chatcommands.lua")
|
||||
dofile(areas.modpath.."/pos.lua")
|
||||
dofile(areas.modpath.."/interact.lua")
|
||||
dofile(areas.modpath.."/legacy.lua")
|
||||
|
||||
areas:load()
|
||||
|
||||
minetest.register_privilege("areas", {description = "Can administer areas"})
|
||||
|
||||
if not minetest.registered_privileges[areas.self_protection_privilege] then
|
||||
minetest.register_privilege(areas.self_protection_privilege,
|
||||
{description = "Can protect areas"})
|
||||
end
|
||||
|
||||
if minetest.setting_getbool("log_mod") then
|
||||
local diffTime = os.clock() - areas.startTime
|
||||
print("[areas] loaded in "..diffTime.."s.")
|
||||
end
|
||||
|
39
interact.lua
Normal file
39
interact.lua
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
-- Gives a player a warning message about a area being protected
|
||||
local function printWarning(name, pos)
|
||||
local owners = areas:getNodeOwners(pos)
|
||||
minetest.chat_send_player(name, ("%s is protected by %s.")
|
||||
:format(minetest.pos_to_string(pos), table.concat(owners, ", ")))
|
||||
end
|
||||
|
||||
if minetest.can_interact then
|
||||
old_can_interact = minetest.can_interact
|
||||
function minetest.can_interact(pos, name)
|
||||
if not areas:canInteract(pos, name) then
|
||||
printWarning(name, pos)
|
||||
return false
|
||||
end
|
||||
return old_can_interact(pos, name)
|
||||
end
|
||||
else
|
||||
local old_node_place = minetest.item_place_node
|
||||
function minetest.item_place_node(itemstack, placer, pointed_thing)
|
||||
-- XXX: buildable_to nodes can mess this up
|
||||
local pos = pointed_thing.above
|
||||
if not areas:canInteract(pos, placer:get_player_name()) then
|
||||
printWarning(placer:get_player_name(), pos)
|
||||
return itemstack -- Abort place.
|
||||
end
|
||||
return old_node_place(itemstack, placer, pointed_thing)
|
||||
end
|
||||
|
||||
local old_node_dig = minetest.node_dig
|
||||
function minetest.node_dig(pos, node, digger)
|
||||
if not areas:canInteract(pos, digger:get_player_name()) then
|
||||
printWarning(digger:get_player_name(), pos)
|
||||
return -- Abort dig.
|
||||
end
|
||||
return old_node_dig(pos, node, digger)
|
||||
end
|
||||
end
|
||||
|
193
internal.lua
Normal file
193
internal.lua
Normal file
@ -0,0 +1,193 @@
|
||||
function areas:player_exists(name)
|
||||
return minetest.auth_table[name] ~= nil
|
||||
end
|
||||
|
||||
-- Save the areas table to a file
|
||||
function areas:save()
|
||||
file, err = io.open(self.filename, "w")
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
file:write(minetest.serialize(self.areas))
|
||||
file:close()
|
||||
end
|
||||
|
||||
-- Load the areas table from the save file
|
||||
function areas:load()
|
||||
file, err = io.open(self.filename, "r")
|
||||
if err then
|
||||
self.areas = self.areas or {}
|
||||
return err
|
||||
end
|
||||
self.areas = minetest.deserialize(file:read("*a"))
|
||||
if type(self.areas) ~= "table" then self.areas = {} end
|
||||
file:close()
|
||||
end
|
||||
|
||||
-- Shorter than the table function
|
||||
function areas:add(owner, name, pos1, pos2, parent)
|
||||
table.insert(areas.areas, {id=table.maxn(self.areas)+1, name=name,
|
||||
pos1=pos1, pos2=pos2, owner=owner, parent=parent})
|
||||
end
|
||||
|
||||
-- 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)
|
||||
if recurse then
|
||||
-- Recursively find child entries and remove them
|
||||
local cids = self:getChildren(id)
|
||||
for _, cid in pairs(cids) do
|
||||
self:removeArea(cid, true)
|
||||
end
|
||||
else
|
||||
-- Update parents
|
||||
local parent = self:getAreaById(id).parent
|
||||
local children = self:getChildren(id)
|
||||
for _, child in pairs(children) do
|
||||
-- The subarea parent will be niled out if the removed area does not have a parent
|
||||
areas.areas[self:getIndexById(child)].parent = parent
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove main entry
|
||||
table.remove(self.areas, self:getIndexById(id))
|
||||
end
|
||||
|
||||
-- Checks if a area between two points is entirely contained by another area
|
||||
function areas:isSubarea(pos1, pos2, id)
|
||||
local area = areas:getAreaById(id)
|
||||
if area then
|
||||
p1, p2 = area.pos1, area.pos2
|
||||
if (pos1.x >= p1.x and pos1.x <= p2.x) and (pos2.x >= p1.x and pos2.x <= p2.x) and
|
||||
(pos1.y >= p1.y and pos1.y <= p2.y) and (pos2.y >= p1.y and pos2.y <= p2.y) and
|
||||
(pos1.z >= p1.z and pos1.z <= p2.z) and (pos2.z >= p1.z and pos2.z <= p2.z) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Returns a table (list) of children of an area given it's identifier
|
||||
function areas:getChildren(id)
|
||||
local children = {}
|
||||
for _, area in pairs(self.areas) do
|
||||
if area.parent and area.parent == id then
|
||||
table.insert(children, area.id)
|
||||
end
|
||||
end
|
||||
return children
|
||||
end
|
||||
|
||||
-- Checks if the user has sufficient privileges.
|
||||
-- If the player is not a administrator it also checks
|
||||
-- if the area intersects other areas that they do not own.
|
||||
-- Also checks the size of the area and if the user already has more than max_areas.
|
||||
function areas:canPlayerAddArea(pos1, pos2, name)
|
||||
--[[
|
||||
if minetest.check_player_privs(name, {areas=true}) then
|
||||
return true
|
||||
end--]]
|
||||
|
||||
-- Check self protection privilege, if it is enabled, and if the area is too big.
|
||||
if (not self.self_protection) or
|
||||
(not minetest.check_player_privs(name, {[areas.self_protection_privilege]=true})) then
|
||||
return false, "Self protection is disabled or you do not have the necessary privilege."
|
||||
end
|
||||
|
||||
if (pos2.x - pos1.x) > self.self_protection_max_size.x or
|
||||
(pos2.y - pos1.y) > self.self_protection_max_size.y or
|
||||
(pos2.z - pos1.z) > self.self_protection_max_size.z then
|
||||
return false, "Area is too big."
|
||||
end
|
||||
|
||||
-- Check number of areas the user has and make sure it not above the max
|
||||
if self.self_protection then
|
||||
local count = 0
|
||||
for _, area in pairs(self.areas) do
|
||||
if area.owner == name then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
if count > self.self_protection_max_areas then
|
||||
return false, "You have reached the maximum amount of areas that you are allowed to protect."
|
||||
end
|
||||
end
|
||||
|
||||
-- Check intersecting areas
|
||||
for _, area in pairs(self.areas) do
|
||||
if (area.pos1.x <= pos2.x and area.pos2.x >= pos1.x) and
|
||||
(area.pos1.y <= pos2.y and area.pos2.y >= pos1.y) and
|
||||
(area.pos1.z <= pos2.z and area.pos2.z >= pos1.z) then
|
||||
--Found an area intersecting with the suplied area
|
||||
if area.owner ~= name then
|
||||
return false, "The area intersects with a area that you do not own."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true, ""
|
||||
end
|
||||
|
||||
-- Given a area returns a string in the format "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children"
|
||||
function areas:toString(area)
|
||||
local message = area.name..
|
||||
"["..area.id.."]: "..area.owner..
|
||||
minetest.pos_to_string(area.pos1)..
|
||||
minetest.pos_to_string(area.pos2)
|
||||
|
||||
local children = areas:getChildren(id)
|
||||
if #children > 0 then
|
||||
message = message..
|
||||
" -> "..table.concat(children, ", ")
|
||||
end
|
||||
return message
|
||||
end
|
||||
|
||||
-- Returns a area given it's identifier
|
||||
function areas:getAreaById(id)
|
||||
for _, area in pairs(self.areas) do
|
||||
if area.id == id then return area end
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns a table index for an area given it's identifier
|
||||
function areas:getIndexById(id)
|
||||
for i, area in pairs(self.areas) do
|
||||
if area.id == id then return i end
|
||||
end
|
||||
end
|
||||
|
||||
-- Re-order areas in table by their identifiers
|
||||
function areas:sort()
|
||||
for k, area in pairs(self.areas) do
|
||||
if area.id ~= k then
|
||||
for _, subarea in pairs(self.areas) do
|
||||
if subarea.parent == area.id then
|
||||
subarea.parent = k
|
||||
end
|
||||
end
|
||||
area.id = k
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks if a player owns an area or a parent of it
|
||||
function areas:isAreaOwner(id, name)
|
||||
cur = self:getAreaById(id)
|
||||
if cur and minetest.check_player_privs(name, {areas=true}) then
|
||||
return true
|
||||
end
|
||||
while cur do
|
||||
if cur.owner == name then
|
||||
return true
|
||||
elseif cur.parent then
|
||||
cur = self:getAreaById(cur.parent)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
145
legacy.lua
Normal file
145
legacy.lua
Normal file
@ -0,0 +1,145 @@
|
||||
-- This file contains functions to convert from
|
||||
-- the old areas format and other compatability code.
|
||||
|
||||
minetest.register_chatcommand("legacy_load_areas", {
|
||||
params = "",
|
||||
description = "Loads, converts, and saves the areas from a legacy save file.",
|
||||
privs = {areas=true, server=true},
|
||||
func = function(name, param)
|
||||
minetest.chat_send_player(name, "Converting areas...")
|
||||
local startTime = os.clock()
|
||||
|
||||
err = areas:legacy_load()
|
||||
if err then
|
||||
minetest.chat_send_player(name, "Error loading legacy file: "..err)
|
||||
return
|
||||
end
|
||||
minetest.chat_send_player(name, "Legacy file loaded.")
|
||||
|
||||
for k, area in pairs(areas.areas) do
|
||||
--New position format
|
||||
areas.areas[k].pos1 = {x=area.x1, y=area.y1, z=area.z1}
|
||||
areas.areas[k].pos2 = {x=area.x2, y=area.y2, z=area.z2}
|
||||
|
||||
areas.areas[k].x1, areas.areas[k].y1,
|
||||
areas.areas[k].z1, areas.areas[k].x2,
|
||||
areas.areas[k].y2, areas.areas[k].z2 =
|
||||
nil, nil, nil, nil, nil, nil
|
||||
|
||||
--Area positions sorting
|
||||
areas.areas[k].pos1, areas.areas[k].pos2 =
|
||||
areas:sortPos(areas.areas[k].pos1, areas.areas[k].pos2)
|
||||
|
||||
--Add name
|
||||
areas.areas[k].name = "unnamed"
|
||||
end
|
||||
minetest.chat_send_player(name, "Table format updated.")
|
||||
|
||||
areas:save()
|
||||
minetest.chat_send_player(name, "Converted areas saved.")
|
||||
minetest.chat_send_player(name, "Finished in "..tostring(os.clock() - startTime).."s.")
|
||||
end})
|
||||
|
||||
-- The old load function from node_ownership (with minor modifications)
|
||||
function areas:legacy_load()
|
||||
local filename = minetest.get_worldpath().."/owners.tbl"
|
||||
tables, err = loadfile(filename)
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
|
||||
tables = tables()
|
||||
for idx = 1, #tables do
|
||||
local tolinkv, tolinki = {}, {}
|
||||
for i, v in pairs(tables[idx]) do
|
||||
if type(v) == "table" and tables[v[1]] then
|
||||
table.insert(tolinkv, {i, tables[v[1]]})
|
||||
end
|
||||
if type(i) == "table" and tables[i[1]] then
|
||||
table.insert(tolinki, {i, tables[i[1]]})
|
||||
end
|
||||
end
|
||||
-- link values, first due to possible changes of indices
|
||||
for _, v in ipairs(tolinkv) do
|
||||
tables[idx][v[1]] = v[2]
|
||||
end
|
||||
-- link indices
|
||||
for _, v in ipairs(tolinki) do
|
||||
tables[idx][v[2]], tables[idx][v[1]] = tables[idx][v[1]], nil
|
||||
end
|
||||
end
|
||||
self.areas = tables[1]
|
||||
end
|
||||
|
||||
-- Returns the name of the first player that owns an area
|
||||
function areas.getNodeOwnerName(pos)
|
||||
for _, area in pairs(areas.areas) do
|
||||
p1, p2 = area.pos1, area.pos2
|
||||
if pos.x >= p1.x and pos.x <= p2.x and
|
||||
pos.y >= p1.y and pos.y <= p2.y and
|
||||
pos.z >= p1.z and pos.z <= p2.z then
|
||||
if area.owner ~= nil then
|
||||
return area.owner
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Checks if a node is owned by you
|
||||
function areas.isNodeOwner(pos, name)
|
||||
if minetest.check_player_privs(name, {areas=true}) then
|
||||
return true
|
||||
end
|
||||
for _, area in pairs(areas.areas) do
|
||||
p1, p2 = area.pos1, area.pos2
|
||||
if pos.x >= p1.x and pos.x <= p2.x and
|
||||
pos.y >= p1.y and pos.y <= p2.y and
|
||||
pos.z >= p1.z and pos.z <= p2.z then
|
||||
if name == area.owner then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
IsPlayerNodeOwner = areas.isNodeOwner
|
||||
GetNodeOwnerName = areas.getNodeOwnerName
|
||||
HasOwner = areas.hasOwner
|
||||
|
||||
if areas.legacy_table then
|
||||
owner_defs = {}
|
||||
setmetatable(owner_defs, {
|
||||
__index = function(table, key)
|
||||
local a = rawget(areas.areas, key)
|
||||
if a then
|
||||
a.x1 = a.pos1.x
|
||||
a.y1 = a.pos1.y
|
||||
a.z1 = a.pos1.z
|
||||
a.x2 = a.pos2.x
|
||||
a.y2 = a.pos2.y
|
||||
a.z2 = a.pos2.z
|
||||
a.pos1, a.pos2 = nil, nil
|
||||
end
|
||||
return a
|
||||
end,
|
||||
__newindex = function(table, key, value)
|
||||
if rawget(areas.areas, key) ~= nil then
|
||||
local a = value
|
||||
a.pos1, a.pos2 = {}, {}
|
||||
a.pos1.x = a.x1
|
||||
a.pos1.y = a.y1
|
||||
a.pos1.z = a.z1
|
||||
a.pos2.x = a.x2
|
||||
a.pos2.y = a.y2
|
||||
a.pos2.z = a.z2
|
||||
a.x1, a.y1, a.z1, a.x2, a.y2, a.z2
|
||||
= nil, nil, nil, nil, nil, nil
|
||||
a.name = a.name or "unnamed"
|
||||
return rawset(areas.areas, key, a);
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
249
pos.lua
Normal file
249
pos.lua
Normal file
@ -0,0 +1,249 @@
|
||||
|
||||
-- I could depend on WorldEdit for this, but you need to have the 'worldedit'
|
||||
-- permission to use those commands and you don't have /area_pos{1,2} [x y z|x,y,z]
|
||||
-- Since this is mostly copied from WorldEdit it is licensed under the AGPL.
|
||||
|
||||
areas.marker1 = {}
|
||||
areas.marker2 = {}
|
||||
areas.set_pos = {}
|
||||
areas.pos1 = {}
|
||||
areas.pos2 = {}
|
||||
|
||||
minetest.register_chatcommand("select_area", {
|
||||
params = "<id>",
|
||||
description = "Select a area by id.",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local id = tonumber(param)
|
||||
if not id then
|
||||
minetest.chat_send_player(name, "Invalid usage, see /help select_area.")
|
||||
end
|
||||
|
||||
for k, area in pairs(areas.areas) do
|
||||
if area.id == id then
|
||||
areas:setPos1(name, area.pos1)
|
||||
areas:setPos2(name, area.pos2)
|
||||
minetest.chat_send_player(name, "Area "..tostring(id).." selected.")
|
||||
return
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(name, "The area "..tostring(id).." does not exist.")
|
||||
end})
|
||||
|
||||
minetest.register_chatcommand("area_pos1", {
|
||||
params = "[X Y Z|X,Y,Z]",
|
||||
description = "Set area protection region position 1 to the player's location or the one specified",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local pos = {}
|
||||
local found, _, x, y, z = param:find("^(-?%d+)[%s%,]+(-?%d+)[%s%,]+(-?%d+)$")
|
||||
if found then
|
||||
pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
|
||||
elseif param == "" then
|
||||
player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
pos = player:getpos()
|
||||
else
|
||||
minetest.chat_send_player(name, "Unable to get position")
|
||||
return
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name, "Invalid usage, see /help no_pos1")
|
||||
end
|
||||
pos.x, pos.y, pos.z = math.floor(pos.x + 0.5), math.floor(pos.y + 0.5), math.floor(pos.z + 0.5)
|
||||
areas:setPos1(name, pos)
|
||||
minetest.chat_send_player(name, "Area position 1 set to " .. minetest.pos_to_string(pos))
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("area_pos2", {
|
||||
params = "[X Y Z|X,Y,Z]",
|
||||
description = "Set area protection region position 2 to the player's location or the one specified",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local pos = {}
|
||||
local found, _, x, y, z = param:find("^(-?%d+)[%s%,]+(-?%d+)[%s%,]+(-?%d+)$")
|
||||
if found then
|
||||
pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
|
||||
elseif param == "" then
|
||||
player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
pos = player:getpos()
|
||||
else
|
||||
minetest.chat_send_player(name, "Unable to get position")
|
||||
return
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name, "Invalid usage, see /help no_pos2")
|
||||
end
|
||||
pos.x, pos.y, pos.z = math.floor(pos.x + 0.5), math.floor(pos.y + 0.5), math.floor(pos.z + 0.5)
|
||||
areas:setPos2(name, pos)
|
||||
minetest.chat_send_player(name, "Area position 2 set to " .. minetest.pos_to_string(pos))
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("area_pos", {
|
||||
params = "set/set1/set2/get",
|
||||
description = "Set area protection region, position 1, or position 2 by punching nodes, or display the region",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
if param == "set" then -- Set both area positions
|
||||
areas.set_pos[name] = "pos1"
|
||||
minetest.chat_send_player(name, "Select positions by punching two nodes")
|
||||
elseif param == "set1" then -- Set area position 1
|
||||
areas.set_pos[name] = "pos1only"
|
||||
minetest.chat_send_player(name, "Select position 1 by punching a node")
|
||||
elseif param == "set2" then -- Set area position 2
|
||||
areas.set_pos[name] = "pos2"
|
||||
minetest.chat_send_player(name, "Select position 2 by punching a node")
|
||||
elseif param == "get" then -- Display current area positions
|
||||
if areas.pos1[name] ~= nil then
|
||||
minetest.chat_send_player(name, "Position 1: " .. minetest.pos_to_string(areas.pos1[name]))
|
||||
else
|
||||
minetest.chat_send_player(name, "Position 1 not set")
|
||||
end
|
||||
if areas.pos2[name] ~= nil then
|
||||
minetest.chat_send_player(name, "Position 2: " .. minetest.pos_to_string(areas.pos2[name]))
|
||||
else
|
||||
minetest.chat_send_player(name, "Position 2 not set")
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name, "Unknown subcommand: " .. param)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
function areas:getPos1(playerName)
|
||||
return areas.pos1[playerName]
|
||||
end
|
||||
|
||||
function areas:getPos2(playerName)
|
||||
return areas.pos2[playerName]
|
||||
end
|
||||
|
||||
function areas:setPos1(playerName, pos)
|
||||
areas.pos1[playerName] = pos
|
||||
areas.markPos1(playerName)
|
||||
end
|
||||
|
||||
function areas:setPos2(playerName, pos)
|
||||
areas.pos2[playerName] = pos
|
||||
areas.markPos2(playerName)
|
||||
end
|
||||
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, puncher)
|
||||
local name = puncher:get_player_name()
|
||||
if name ~= "" and areas.set_pos[name] ~= nil then --currently setting position
|
||||
if areas.set_pos[name] == "pos1" then --setting position 1
|
||||
areas.pos1[name] = pos
|
||||
areas.markPos1(name)
|
||||
areas.set_pos[name] = "pos2" --set position 2 on the next invocation
|
||||
minetest.chat_send_player(name, "Position 1 set to " .. minetest.pos_to_string(pos))
|
||||
elseif areas.set_pos[name] == "pos1only" then --setting position 1 only
|
||||
areas.pos1[name] = pos
|
||||
areas.markPos1(name)
|
||||
areas.set_pos[name] = nil --finished setting positions
|
||||
minetest.chat_send_player(name, "Position 1 set to " .. minetest.pos_to_string(pos))
|
||||
elseif areas.set_pos[name] == "pos2" then --setting position 2
|
||||
areas.pos2[name] = pos
|
||||
areas.markPos2(name)
|
||||
areas.set_pos[name] = nil --finished setting positions
|
||||
minetest.chat_send_player(name, "Position 2 set to " .. minetest.pos_to_string(pos))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Modifies positions `pos1` and `pos2` so that each component of `pos1`
|
||||
-- is less than or equal to its corresponding component of `pos2`,
|
||||
-- returning two new positions
|
||||
function areas:sortPos(pos1, pos2)
|
||||
if pos1.x > pos2.x then
|
||||
pos2.x, pos1.x = pos1.x, pos2.x
|
||||
end
|
||||
if pos1.y > pos2.y then
|
||||
pos2.y, pos1.y = pos1.y, pos2.y
|
||||
end
|
||||
if pos1.z > pos2.z then
|
||||
pos2.z, pos1.z = pos1.z, pos2.z
|
||||
end
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
-- Rounds a position to the nearest integer
|
||||
function areas:roundPos(pos)
|
||||
pos.x = math.floor(pos.x+0.5)
|
||||
pos.y = math.floor(pos.y+0.5)
|
||||
pos.z = math.floor(pos.z+0.5)
|
||||
return pos
|
||||
end
|
||||
|
||||
-- Marks area position 1
|
||||
areas.markPos1 = function(name)
|
||||
local pos = areas.pos1[name]
|
||||
if areas.marker1[name] ~= nil then -- Marker already exists
|
||||
areas.marker1[name]:remove() -- Remove marker
|
||||
areas.marker1[name] = nil
|
||||
end
|
||||
if pos ~= nil then -- Add marker
|
||||
areas.marker1[name] = minetest.add_entity(pos, "areas:pos1")
|
||||
areas.marker1[name]:get_luaentity().active = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Marks area position 2
|
||||
areas.markPos2 = function(name)
|
||||
local pos = areas.pos2[name]
|
||||
if areas.marker2[name] ~= nil then -- Marker already exists
|
||||
areas.marker2[name]:remove() -- Remove marker
|
||||
areas.marker2[name] = nil
|
||||
end
|
||||
if pos ~= nil then -- Add marker
|
||||
areas.marker2[name] = minetest.add_entity(pos, "areas:pos2")
|
||||
areas.marker2[name]:get_luaentity().active = true
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("areas:pos1", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"areas_pos1.png", "areas_pos1.png",
|
||||
"areas_pos1.png", "areas_pos1.png",
|
||||
"areas_pos1.png", "areas_pos1.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if self.active == nil then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
self.object:remove()
|
||||
local name = hitter:get_player_name()
|
||||
areas.marker1[name] = nil
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity("areas:pos2", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"areas_pos2.png", "areas_pos2.png",
|
||||
"areas_pos2.png", "areas_pos2.png",
|
||||
"areas_pos2.png", "areas_pos2.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if self.active == nil then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
self.object:remove()
|
||||
local name = hitter:get_player_name()
|
||||
areas.marker2[name] = nil
|
||||
end,
|
||||
})
|
||||
|
35
settings.lua
Normal file
35
settings.lua
Normal file
@ -0,0 +1,35 @@
|
||||
local worldpath = minetest.get_worldpath()
|
||||
|
||||
local function setting_getbool_default(setting, default)
|
||||
local value = minetest.setting_getbool(setting)
|
||||
if value == nil then
|
||||
value = default
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
areas.filename =
|
||||
minetest.setting_get("areas.filename") or worldpath.."/areas.dat"
|
||||
|
||||
-- Allow players with a privilege create their own areas within the max_size and number
|
||||
areas.self_protection =
|
||||
setting_getbool_default("areas.self_protection", false)
|
||||
areas.self_protection_privilege =
|
||||
minetest.setting_get("areas.self_protection_privilege") or "interact"
|
||||
areas.self_protection_max_size =
|
||||
minetest.setting_get_pos("areas.self_protection_max_size") or {x=50, y=100, z=50}
|
||||
areas.self_protection_max_areas =
|
||||
tonumber(minetest.setting_get("areas.self_protection_max_areas")) or 3
|
||||
|
||||
-- Register compatability functions for node_ownership.
|
||||
-- legacy_table (owner_defs) compatibility is untested
|
||||
-- and can not be used if security_safe_mod_api is on.
|
||||
areas.legacy_table =
|
||||
setting_getbool_default("areas.legacy_table", false)
|
||||
|
||||
-- Prevent players from punching nodes in a protected area.
|
||||
-- Usefull for things like delayers, usualy annoying and
|
||||
-- prevents usage of things like buttons.
|
||||
areas.protect_punches =
|
||||
setting_getbool_default("areas.protect_punches", false)
|
||||
|
BIN
textures/areas_pos1.png
Normal file
BIN
textures/areas_pos1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 142 B |
BIN
textures/areas_pos2.png
Normal file
BIN
textures/areas_pos2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 157 B |
Loading…
Reference in New Issue
Block a user