Compare commits

...

58 Commits

Author SHA1 Message Date
2184118a4b Fix crash 2024-09-15 23:09:05 +02:00
0c9815460a Merge remote-tracking branch 'upstream/master' 2024-09-15 08:24:21 +02:00
Luke aka SwissalpS
c2e3d073be
Use smallest area at given position (#79)
When checking for permission to interact, currently the smallest id takes precedence in a situation where areas enclose other areas. This makes it complicated to set up private areas within faction areas and vice versa. Same applies for open areas within a bigger private area.

When multiple areas have the same volume, players seem to agree that the most recently protected area should also take precedence.
Since we can't guarantee that the largest id is the most recent, we still use that info as we have no other way to determine age.
2024-09-11 22:00:47 +02:00
1F616EMO~nya
9a5cdb2822
Allow relative coordinates in /area_pos[12] (#77) 2024-08-07 21:33:36 +02:00
1F616EMO~nya
9024be24eb
Fix deprecated hud_elem_type field warning (#78)
Co-authored-by: cx384 <cx384@proton.me>
2024-07-17 18:18:21 +02:00
Alexander Chibrikin
2deb6770f4
Add russian translation (#68)
* Add ru locale.

* Fixed review remarks

---------

Co-authored-by: ssdaniel24 <bo7oaonteg2m@mail.ru>
2024-06-13 17:26:00 +02:00
tour
0bad0ec0ae
Allow custom checks before allowing a player to protect new areas (#74) 2024-03-22 22:26:12 +01:00
SmallJoker
c044d49d21
Short summary print in /list_areas for admins (#72) 2024-02-24 11:46:45 +01:00
1F616EMO~nya
2c900ef2cd
zh_TW and zh_CN translation (#71) 2024-02-15 18:35:43 +01:00
fluxionary
f4bb8eb29b
Include the endpoint position in the area size check (#70) 2024-02-09 21:25:23 +01:00
2fda1adbd7 Merge remote-tracking branch 'upstream/master' 2023-11-22 23:04:43 +01:00
Niklp
cba647225c
Add missing placeholder in French translation (#66) 2023-08-28 12:22:40 +02:00
fluxionary
c8cf4aba92
Fix orthography (it's -> its) (#65) 2023-08-26 11:40:59 +02:00
64b19c2594 Merge remote-tracking branch 'upstream/master' 2023-06-04 23:07:43 +02:00
fluxionary
d464d3484f
Fix chat command typo (#64) 2022-11-07 19:59:25 +01:00
a8cacee8cc Fixed a minor bug introduced when I created openfarming, which skips the faction test if an area is openfarming.
And not return false if not a player(ie:mobs api) or unknown node, continue iterate on others areas(minor bug, generaly if 1 area it's protected).
2022-07-09 02:49:12 +02:00
76f8ccf8ab Add morefarming and improve farming support on openfarming areas 2020-10-14 22:33:09 +02:00
02b23b0371 Merge remote-tracking branch 'upstream/master' 2020-09-08 18:45:48 +02:00
Louis Royer
4018c0d204
Add privilege translations and update translations (#49)
+ Add new french translation strings
2020-09-07 17:47:18 +02:00
Kalio_42
c167b30ebf
Adapt to the new version of playerfactions (#48)
Enable to open an area to many factions
    Update to adapt to the new version of playerfactions mod, with its new multi-faction mode
2020-09-05 18:49:17 +02:00
4eeb2a9d11 Merge remote-tracking branch 'upstream/master' 2020-08-29 20:29:29 +02:00
luk3yx
a9c05f0e38
Use JSON to store areas (#47)
This works around a limit of ~21000 areas because of limits in 'minetest.deserialize()'. Files will be migrated to the new format on first change.
2020-08-28 12:54:48 +02:00
99408df96a Delete unused textures 2020-07-05 17:10:03 +02:00
42cde6a494 Merge branch 'nalc-1.2-dev' 2020-07-05 17:02:34 +02:00
e0783cf8bf Corrige crash au démarrage 2020-06-15 01:10:47 +02:00
0b2baacb92 Merge remote-tracking branch 'upstream/master' into nalc-1.2-dev 2020-06-14 23:38:20 +02:00
Hamlet
142a723eb2
Add Italian translation (#45) 2020-06-14 18:59:48 +02:00
Buckaroo Banzai
66bd6a9b1d
Add luacheck CI and drop node_ownership remnants (#44)
`node_ownership` is long dead, and pollutes the global namespace. Support dropped after ~7 years.

Co-authored-by: BuckarooBanzay <BuckarooBanzay@users.noreply.github.com>
2020-06-08 21:52:11 +02:00
MoNTE48
aa3e35acbe /areas_cleanup for removing ownerless areas 2020-06-04 20:36:12 +02:00
MoNTE48
800a93f5ee Minor cleanup 2020-06-04 20:36:12 +02:00
Louis Royer
448fe3ebf1 Rework on messages displayed
Co-authored-by: SmallJoker <SmallJoker@users.noreply.github.com>
2020-03-19 22:46:22 +01:00
Louis Royer
2f4eddd324 Add french translation 2020-03-19 22:46:22 +01:00
Louis Royer
4179917eb4 Add translation support 2020-03-19 22:46:22 +01:00
54c504fa0d [areas] Remet le HUD comme avant la modif du feae996 2019-12-30 01:02:36 +01:00
26d6f56485 Ajoute description privilège \"megabuilder\" pour admins d'areas 2019-12-28 15:26:21 +01:00
57f20bb25f Ajoute infos privilège "megabuilder" dans la commande /area_info 2019-12-28 15:07:13 +01:00
feae9967dc Ajoute certaines modifications venant de MFF + ajustement HUD
* Aires openfarming
* Privilège megabuilder
* HUD légèrement modifié pour laisser de la place à celui de factions
2019-12-28 02:43:12 +01:00
Alden Peeters
aca830fd22 Add support for playerfactions mod (#37)
Add faction indicator to HUD
2019-09-21 15:33:42 +02:00
SmallJoker
95c1165e28 Add and parse from settingtypes.txt. Update readme 2019-07-11 20:02:32 +02:00
Panquesito7
5527dc8945 Replace deprecated functions with newer ones (#36)
This commit replaces a few deprecated calls to getpos with get_pos
2019-07-10 20:26:42 +02:00
Auke Kok
6218e5884d Limit areas to -30992,30992 due to MABLOCK_SIZE=16
Internally, when allocating an AreaStore, the limits are required
to be within the last full block, and so, you cannot create one "on"
the edge, as it will trigger an exception. When limited to the last
full mapblock, it all works fine.
2019-04-03 11:10:59 -07:00
Auke Kok
cfd4bb2423 Last of the luacheck cleanups. 2019-04-03 10:26:20 -07:00
Auke Kok
6e2b9a0a51 Needs to be defined earlier. 2019-04-03 10:24:02 -07:00
Auke Kok
024424ee8b Also limit chatcommand area_pos[12] positions. 2019-04-03 10:17:47 -07:00
Vanessa Dannenberg
f70600db30 oops, commas would be nice. 2019-04-02 16:06:19 -07:00
Vanessa Dannenberg
9508a004d0 fix posLimit(pos) to process and return a position table 2019-04-02 14:53:54 -07:00
Auke Kok
630bdefd98 Don't allow areas to cross outside mapgen world limits.
Limit any area to within [-31000,31000].
2019-04-01 21:28:03 -07:00
Auke Kok
a303abe51b Add template .luacheckrc 2019-04-01 21:22:31 -07:00
Tai Kedzierski
7b51f84404 Limit recalculations.
Setting based - limit area recalculation and allow tuning of the
interval.
2019-04-01 21:21:31 -07:00
sofar
1bbb997c7a
Add callbacks for area operations 2019-04-01 20:28:50 -07:00
red-001
09c030352f Use the new minetest.safe_file_write API if possible when saving database. 2019-04-01 20:27:17 -07:00
sofar
2637876555
Fix pos big (#1)
* fix pos being to big

* improve

* make the change instead in hud.lua
2019-04-01 20:24:59 -07:00
sys4-fr
c4d0fe020f Corrige lecture de paramètre de configuration 2018-12-24 02:22:29 +01:00
AntumDeluge
289d0e623c Update usage of settings API 2017-06-09 13:00:12 -04:00
Jordan Irwin
d3d43d9511 Reference 'settings.lua' instead of 'config.lua' (#21) 2017-05-06 13:41:45 -04:00
Tai @ Flex
6080ff065e Add API for adding areas to HUD 2016-12-19 16:36:37 +00:00
ShadowNinja
23f81f6278 Add mod.conf 2016-12-04 04:12:27 +00:00
Dorian Wouters
7cb8787beb Use get_auth_handler().get_auth() instead of auth_table
minetest.auth_table is an implementation detail of the default auth handler.
No guarantee is made that it even exists and using this table directly is incompatible
with custom auth handlers.  Instead, use the proper auth handler API.
2016-09-03 21:05:52 -04:00
30 changed files with 1687 additions and 553 deletions

17
.github/workflows/luacheck.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: luacheck
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: apt
run: sudo apt-get install -y luarocks
- name: luacheck install
run: luarocks install --local luacheck
- name: luacheck run
run: $HOME/.luarocks/bin/luacheck ./

24
.luacheckrc Normal file
View File

@ -0,0 +1,24 @@
unused_args = false
read_globals = {
"DIR_DELIM",
"core",
"dump",
"vector", "nodeupdate",
"VoxelManip", "VoxelArea",
"PseudoRandom", "ItemStack",
"AreaStore",
"default",
"factions",
table = { fields = { "copy", "getn", "indexof" } }
}
globals = {
"minetest",
-- mod namespace
"areas"
}
files["legacy.lua"] = {
ignore = {"512"}
}

View File

@ -1,21 +1,34 @@
Areas mod for Minetest 0.4.8+ Areas mod for Minetest
============================= ======================
Dependencies
------------
Minetest 5.0.0+ is recommended, but 0.4.16+ should work as well.
Minetest 5.0.0+
Optional support for following mods:
* [playerfactions](https://git.leagueh.xyz/katp32/playerfactions/) by [katp32](https://git.leagueh.xyz/katp32) & [Kalio_42](https://git.leagueh.xyz/Kalio_42)
Configuration Configuration
------------- -------------
If you wish to specify configuration options, such as whether players are Open the tab `Settings -> All Settings -> Mods -> areas` to get a list of all
allowed to protect their own areas with the `protect` command (disabled by possible settings.
default), you should check config.lua and set the appropriate settings in your
server's configuration file (probably `minetest.conf`). For server owners: Check `settingtypes.txt` and modify your `minetest.conf`
according to the wanted setting changes.
Tutorial Tutorial
-------- --------
To protect an area you must first set the corner positions of the area. 1) Specify the corner positions of the area you would like to protect.
In order to set the corner positions you can run: Use one of the following commands:
* `/area_pos set` and punch the two corner nodes to set them. * `/area_pos set` and punch the two corner nodes to set them.
* `/area_pos set1/set2` and punch only the first or second corner node to * `/area_pos set1/set2` and punch only the first or second corner node to
set them one at a time. set them one at a time.
@ -23,25 +36,25 @@ In order to set the corner positions you can run:
* `/area_pos1/2 X Y Z` to set one of the positions to the specified * `/area_pos1/2 X Y Z` to set one of the positions to the specified
coordinates. coordinates.
Once you have set the border positions you can protect the area by running one 2) Protect the selected area by running one of the following commands:
of the following commands:
* `/set_owner <OwnerName> <AreaName>` -- If you have the `areas` privilege. * `/set_owner <OwnerName> <AreaName>` -- If you have the `areas` privilege.
* `/protect <AreaName>` -- If you have the `areas` privilege or the server * `/protect <AreaName>` -- If you have the `areas` privilege or the server
administrator has enabled area self-protection. administrator has enabled area self-protection.
The area name is used only for informational purposes (so that you know what The area name is used only for informational purposes and has no functional
an area is for). It is not used for any other purpose. importance.
For example: `/set_owner SomePlayer Mese city` For example: `/set_owner SomePlayer Mese city`
Now that you own an area you may want to add sub-owners to it. You can do this 3) You now own an area. You may now add sub-owners to it if you want to (see command `/add_owner`). Before using the `/add_owner` command you have to
with the `add_owner` command. Anyone with an area can use the `add_owner` select the corners of the sub-area as you did in step 1.
command on their areas. Before using the `add_owner` command you have to
select the corners of the sub-area as you did for `set_owner`. If your markers If your markers are still around your original area and you want to grant
are still around your original area and you want to grant access to your access to your entire area you will not have to re-set them. Use `/select_area` to place the markers at the corners of an existing area if you've reset your
entire area you will not have to re-set them. You can also use `select_area` to
place the markers at the corners of an existing area if you've reset your
markers and want to grant access to a full area. markers and want to grant access to a full area.
The `add_owner` command expects three arguments:
The `/add_owner` command expects three arguments:
1. The ID number of the parent area (the area that you want to add a 1. The ID number of the parent area (the area that you want to add a
sub-area to). sub-area to).
2. The name of the player that will own the sub-area. 2. The name of the player that will own the sub-area.
@ -74,7 +87,7 @@ Commands
* `/remove_area <ID>` -- Removes an area that you own. Any sub-areas of that * `/remove_area <ID>` -- Removes an area that you own. Any sub-areas of that
area are made sub-areas of the removed area's parent, if it exists. area are made sub-areas of the removed area's parent, if it exists.
If the removed area has no parent it's sub-areas will have no parent. If the removed area has no parent its sub-areas will have no parent.
* `/recursive_remove_areas <ID>` -- Removes an area and all sub-areas of it. * `/recursive_remove_areas <ID>` -- Removes an area and all sub-areas of it.
@ -94,11 +107,21 @@ Commands
* `/area_pos2 [X,Y,Z|X Y Z]` -- Sets area position two to your position or * `/area_pos2 [X,Y,Z|X Y Z]` -- Sets area position two to your position or
the one supplied. the one supplied.
* `/areas_cleanup` -- Removes all ownerless areas.
Useful for cleaning after user deletion, for example using /remove_player.
* `/area_open <ID>` -- Toggle open/closed the specified area for everyone.
* `/area_faction_open <ID> <faction>` -- Toggle open/closed the specified
area for members of the faction. Factions are created and managed by
playerfactions mod.
License License
------- -------
Copyright (C) 2013 ShadowNinja Copyright (C) 2013-2017 ShadowNinja
Copyright (C) 2015-2020 various contributors
Licensed under the GNU LGPL version 2.1 or later. Licensed under the GNU LGPL version 2.1 or later.
See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt

206
api.lua
View File

@ -1,14 +1,70 @@
local hudHandlers = {}
--plants to place in openfarming ---plants to place in openfarming
local plants = { ["farming:blueberries"]="air", ["farming:carrot"]="air", ["farming:coffee_beans"]="air", ["farming:corn"]="air", ["farming:cucumber"]="air", local plants = {
["farming:melon_slice"]="air", ["farming:potato"]="air", ["farming:pumpkin_slice"]="air", ["farming:raspberries"]="air", ["farming:rhubarb"]="air", ["farming:beetroot"]="air", ["farming:blueberries"]="air", ["farming:cabbage"]="air",
["farming:tomato"]="air", ["farming:seed_cotton"]="air", ["farming:seed_wheat"]="air",["default:papyrus"]="air", ["farming:trellis"]="air", ["farming:carrot"]="air", ["farming:chili_pepper"]="air", ["farming:coffee_beans"]="air",
["farming:grapes"]="farming:trellis", ["farming:beanpole"]="air", ["farming:beans"]="farming:beanpole", ["farming:corn"]="air", ["farming:cucumber"]="air", ["farming:garlic_clove"]="air",
} ["farming:melon_slice"]="air", ["farming:onion"]="air", ["default:papyrus"]="air",
["farming:pea_pod"]="air", ["farming:peppercorn"]="air", ["farming:pineapple_top"]="air",
["farming:potato"]="air", ["farming:pumpkin_slice"]="air", ["farming:raspberries"]="air",
["farming:rhubarb"]="air",
["farming:seed_barley"]="air", ["farming:seed_cotton"]="air", ["farming:seed_hemp"]="air",
["farming:seed_mint"]="air", ["farming:seed_oat"]="air", ["farming:seed_rice"]="air",
["farming:seed_rye"]="air", ["farming:seed_wheat"]="air",
["farming:tomato"]="air",
["farming:trellis"]="air", ["farming:grapes"]="farming:trellis",
["farming:beanpole"]="air", ["farming:beans"]="farming:beanpole",
["morefarming:seed_wildcarrot"]="air", ["morefarming:seed_teosinte"]="air",
["morefarming:seed_carrot"]="air", ["morefarming:seed_corn"]="air",
}
areas.registered_protection_conditions = {}
areas.registered_on_adds = {}
areas.registered_on_removes = {}
areas.registered_on_moves = {}
areas.callback_origins = {}
function areas:registerProtectionCondition(func)
table.insert(areas.registered_protection_conditions, func)
local debug_info = debug.getinfo(func, "S")
areas.callback_origins[func] = {
mod = core.get_current_modname() or "??",
source = debug_info.short_src or "??",
line = debug_info.linedefined or "??"
}
end
function areas:registerOnAdd(func)
table.insert(areas.registered_on_adds, func)
end
function areas:registerOnRemove(func)
table.insert(areas.registered_on_removes, func)
end
function areas:registerOnMove(func)
table.insert(areas.registered_on_moves, func)
end
--- Adds a function as a HUD handler, it will be able to add items to the Areas HUD element.
function areas:registerHudHandler(handler)
table.insert(hudHandlers, handler)
end
function areas:getExternalHudEntries(pos)
local areas = {}
for _, func in pairs(hudHandlers) do
func(pos, areas)
end
return areas
end
--- 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 res = {} local res = {}
if self.store then if self.store then
local a = self.store:get_areas_for_pos(pos, false, true) local a = self.store:get_areas_for_pos(pos, false, true)
for store_id, store_area in pairs(a) do for store_id, store_area in pairs(a) do
@ -58,22 +114,42 @@ function areas:getAreasIntersectingArea(pos1, pos2)
return res return res
end end
-- Checks if the area is unprotected or owned by you -- Returns smallest area at position and its id or nil.
-- If multiple areas have the same volume, larger id takes precedence.
function areas:getSmallestAreaAtPos(pos)
local smallest_area, smallest_id, volume
local smallest_volume = math.huge
for id, area in pairs(self:getAreasAtPos(pos)) do
volume = (area.pos2.x - area.pos1.x + 1)
* (area.pos2.y - area.pos1.y + 1)
* (area.pos2.z - area.pos1.z + 1)
if smallest_volume >= volume then
smallest_area = area
smallest_id = id
smallest_volume = volume
end
end
return smallest_area, smallest_id
end
-- Checks if the area is unprotected, open, owned by player
-- or player is part of faction of smallest area at position.
function areas:canInteract(pos, name) function areas:canInteract(pos, name)
if minetest.check_player_privs(name, self.adminPrivs) then if minetest.check_player_privs(name, self.adminPrivs) then
return true return true
end end
local owned = false local area = self:getSmallestAreaAtPos(pos)
if pos == nil then return not owned end -- pour éviter crash avec nénuphar -- No area, player owns it or area is open
for _, area in pairs(self:getAreasAtPos(pos)) do if not area
if area.owner == name or area.open then or area.owner == name
return true or area.open
elseif area.openfarming then then
-- if area is openfarming return true
local node = minetest.get_node(pos).name elseif area.openfarming then
if not minetest.registered_nodes[node] then return false end -- if area is openfarming
local player = minetest.get_player_by_name(name) local player = minetest.get_player_by_name(name)
if not player then return false end local node = minetest.get_node(pos).name
if player and minetest.registered_nodes[node] then
local wstack = player:get_wielded_item():get_name() local wstack = player:get_wielded_item():get_name()
if wstack == "" then wstack = "hand" end if wstack == "" then wstack = "hand" end
@ -86,13 +162,26 @@ function areas:canInteract(pos, name)
if plants[wstack] ~= nil and plants[wstack] == node then if plants[wstack] ~= nil and plants[wstack] == node then
return true return true
end end
end
owned = true elseif areas.factions_available and area.faction_open then
if (factions.version or 0) < 2 then
local faction_name = factions.get_player_faction(name)
if faction_name then
for _, fname in ipairs(area.faction_open or {}) do
if faction_name == fname then
return true
end
end
end
else else
owned = true for _, fname in ipairs(area.faction_open or {}) do
if factions.player_is_in_faction(fname, name) then
return true
end
end
end end
end end
return not owned return false
end end
-- Returns a table (list) of all players that own an area -- Returns a table (list) of all players that own an area
@ -108,7 +197,7 @@ 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 if 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.
@ -153,74 +242,3 @@ function areas:canInteractInArea(pos1, pos2, name, allow_open)
-- intersecting areas and they are all owned by the player. -- intersecting areas and they are all owned by the player.
return true return true
end end
function areas:canMakeArea(pos1, pos2, name) --MFF crabman(25/02/2016) fix areas in areas
if name and minetest.check_player_privs(name, self.adminPrivs) then
return true
end
areas:sortPos(pos1, pos2)
local id_areas_intersect = {}
local areas = self:getAreasIntersectingArea(pos1, pos2)
if not areas then return true end
for id, area in pairs(areas) do
if area.owner == name and self:isSubarea(pos1, pos2, id) then
return true
end
if not area.open and not self:isAreaOwner(id, name) then
table.insert(id_areas_intersect, id)
end
end
if #id_areas_intersect > 0 then
return false, id_areas_intersect[1]
end
return true
end
--MFF crabman(5/03/2016 ) return special area pos if a spawn is set.
--1 party (2 party in beds mod)
function areas:getSpawn(pos)
for _, area in pairs(areas:getAreasAtPos(pos)) do
if area.spawn and area.spawn.x then
return area.spawn
end
end
return nil
end
--MFF DEBUT crabman(17/09/2015 ) respawn player in special area(event) if a spawn is set.
--1 party (2 party in beds mod)
local dead_players = {}
minetest.register_on_dieplayer(function(player)
local player_name = player:get_player_name()
if not player_name then return end
local pos = player:getpos()
if pos then
dead_players[player_name] = pos
end
end)
function areas:onRespawn(player)
local player_name = player:get_player_name()
if not player_name or not dead_players[player_name] then return false end
local pos = dead_players[player_name]
dead_players[player_name] = nil
if pos then
for _, area in pairs(areas:getAreasAtPos(pos)) do
if area.spawn then
player:setpos(area.spawn)
return true
end
end
end
return false
end
--FIN

64
api.md Normal file
View File

@ -0,0 +1,64 @@
Areas mod API
===
API list
---
* `areas:registerHudHandler(handler)` - Registers a handler to add items to the Areas HUD. See [HUD](#hud).
* `areas:registerProtectionCondition(func(pos1, pos2, name))` -
See [Protection Conditions](#Protection-Conditions)
* `areas:registerOnAdd(func(id, area))`
* `areas:registerOnRemove(func(id))`
* `areas:registerOnMove(func(id, area, pos1, pos2))`
Protection Conditions
---
With `areas:registerProtectionCondition(func(pos1, pos2, name))`
you can register rules to control whether to allow or prohibit the creation of an area.
Return values:
* `true` Forcefully allows the area creation. This overwrites the outcome of any
previously executed conditions, including the default ones registered by this mod.
* `false, errMsg` Disable the creation of the area and return an error message.
* `nil` (or no return value) Enable the creation of the area,
unless specified otherwise by the other registered callbacks.
HUD
---
If you are making a protection mod or a similar mod that adds invisible regions
to the world, and you would like then to show up in the areas HUD element, you
can register a callback to show your areas.
HUD handler specification:
* `handler(pos, list)`
* `pos` - The position to check.
* `list` - The list of area HUD elements, this should be modified in-place.
The area list item is a table containing a list of tables with the following fields:
* `id` - An identifier for the area. This should be a unique string in the format `mod:id`.
* `name` - The name of the area.
* `owner` - The player name of the region owner, if any.
All of the fields are optional but at least one of them must be set.
### Example
local function areas_hud_handler(pos, areas)
local val = find_my_protection(pos)
if val then
table.insert(areas, {
id = "mod:"..val.id,
name = val.name,
owner = val.owner,
})
end
end
areas:registerHudHandler(areas_hud_handler)

View File

@ -1,15 +1,16 @@
local S = minetest.get_translator("areas")
minetest.register_chatcommand("protect", { minetest.register_chatcommand("protect", {
params = "<AreaName>", params = S("<AreaName>"),
description = "Protect your own area", description = S("Protect your own area"),
privs = {[areas.config.self_protection_privilege]=true}, privs = {[areas.config.self_protection_privilege]=true},
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
return false, "Invalid usage, see /help protect." return false, S("Invalid usage, see /help @1.", "protect")
end end
local pos1, pos2 = areas:getPos(name) local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then if not (pos1 and pos2) then
return false, "You need to select an area first." return false, S("You need to select an area first.")
end end
minetest.log("action", "/protect invoked, owner="..name.. minetest.log("action", "/protect invoked, owner="..name..
@ -19,38 +20,37 @@ minetest.register_chatcommand("protect", {
local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name) local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
if not canAdd then if not canAdd then
return false, "You can't protect that area: "..errMsg return false, S("You can't protect that area: @1", errMsg)
end end
local id = areas:add(name, param, pos1, pos2, nil) local id = areas:add(name, param, pos1, pos2, nil)
areas:save() areas:save()
return true, "Area protected. ID: "..id return true, S("Area protected. ID: @1", id)
end end
}) })
minetest.register_chatcommand("set_owner", { minetest.register_chatcommand("set_owner", {
params = "<PlayerName> <AreaName>", params = S("<PlayerName>").." "..S("<AreaName>"),
description = "Protect an area beetween two positions and give" description = S("Protect an area between two positions and give"
.." a player access to it without setting the parent of the" .." a player access to it without setting the parent of the"
.." area to any existing area", .." area to any existing area"),
privs = areas.adminPrivs, privs = areas.adminPrivs,
func = function(name, param) func = function(name, param)
local ownerName, areaName = param:match('^(%S+)%s(.+)$') local ownerName, areaName = param:match('^(%S+)%s(.+)$')
if not ownerName then if not ownerName then
return false, "Incorrect usage, see /help set_owner." return false, S("Invalid usage, see /help @1.", "set_owner")
end end
local pos1, pos2 = areas:getPos(name) local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then if not (pos1 and pos2) then
return false, "You need to select an area first." return false, S("You need to select an area first.")
end end
if not areas:player_exists(ownerName) then if not areas:player_exists(ownerName) then
return false, "The player \"" return false, S("The player \"@1\" does not exist.", ownerName)
..ownerName.."\" does not exist."
end end
minetest.log("action", name.." runs /set_owner. Owner = "..ownerName.. minetest.log("action", name.." runs /set_owner. Owner = "..ownerName..
@ -62,34 +62,33 @@ minetest.register_chatcommand("set_owner", {
areas:save() areas:save()
minetest.chat_send_player(ownerName, minetest.chat_send_player(ownerName,
"You have been granted control over area #".. S("You have been granted control over area #@1. "..
id..". Type /list_areas to show your areas.") "Type /list_areas to show your areas.", id))
return true, "Area protected. ID: "..id return true, S("Area protected. ID: @1", id)
end end
}) })
minetest.register_chatcommand("add_owner", { minetest.register_chatcommand("add_owner", {
params = "<ParentID> <Player> <AreaName>", params = S("<ParentID>").." "..S("<PlayerName>").." "..S("<AreaName>"),
description = "Give a player access to a sub-area beetween two" description = S("Give a player access to a sub-area between two"
.." positions that have already been protected," .." positions that have already been protected,"
.." Use set_owner if you don't want the parent to be set.", .." Use set_owner if you don't want the parent to be set."),
func = function(name, param) func = function(name, param)
local pid, ownerName, areaName local pid, ownerName, areaName = param:match('^(%d+) ([^ ]+) (.+)$')
= param:match('^(%d+) ([^ ]+) (.+)$')
if not pid then if not pid then
minetest.chat_send_player(name, "Incorrect usage, see /help add_owner") minetest.chat_send_player(name, S("Invalid usage, see /help @1.", "add_owner"))
return return
end end
local pos1, pos2 = areas:getPos(name) local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then if not (pos1 and pos2) then
return false, "You need to select an area first." return false, S("You need to select an area first.")
end end
if not areas:player_exists(ownerName) then if not areas:player_exists(ownerName) then
return false, "The player \""..ownerName.."\" does not exist." return false, S("The player \"@1\" does not exist.", ownerName)
end end
minetest.log("action", name.." runs /add_owner. Owner = "..ownerName.. minetest.log("action", name.." runs /add_owner. Owner = "..ownerName..
@ -101,52 +100,52 @@ minetest.register_chatcommand("add_owner", {
pid = tonumber(pid) pid = tonumber(pid)
if (not areas:isAreaOwner(pid, name)) or if (not areas:isAreaOwner(pid, name)) or
(not areas:isSubarea(pos1, pos2, pid)) then (not areas:isSubarea(pos1, pos2, pid)) then
return false, "You can't protect that area." return false, S("You can't protect that area.")
end end
local id = areas:add(ownerName, areaName, pos1, pos2, pid) local id = areas:add(ownerName, areaName, pos1, pos2, pid)
areas:save() areas:save()
minetest.chat_send_player(ownerName, minetest.chat_send_player(ownerName,
"You have been granted control over area #".. S("You have been granted control over area #@1. "..
id..". Type /list_areas to show your areas.") "Type /list_areas to show your areas.", id))
return true, "Area protected. ID: "..id return true, S("Area protected. ID: @1", id)
end end
}) })
minetest.register_chatcommand("rename_area", { minetest.register_chatcommand("rename_area", {
params = "<ID> <newName>", params = S("<ID>").." "..S("<newName>"),
description = "Rename a area that you own", description = S("Rename an area that you own"),
func = function(name, param) func = function(name, param)
local id, newName = param:match("^(%d+)%s(.+)$") local id, newName = param:match("^(%d+)%s(.+)$")
if not id then if not id then
return false, "Invalid usage, see /help rename_area." return false, S("Invalid usage, see /help @1.", "rename_area")
end end
id = tonumber(id) id = tonumber(id)
if not id then if not id then
return false, "That area doesn't exist." return false, S("That area doesn't exist.")
end end
if not areas:isAreaOwner(id, name) then if not areas:isAreaOwner(id, name) then
return true, "You don't own that area." return true, S("You don't own that area.")
end end
areas.areas[id].name = newName areas.areas[id].name = newName
areas:save() areas:save()
return true, "Area renamed." return true, S("Area renamed.")
end end
}) })
minetest.register_chatcommand("find_areas", { minetest.register_chatcommand("find_areas", {
params = "<regexp>", params = "<regexp>",
description = "Find areas using a Lua regular expression", description = S("Find areas using a Lua regular expression"),
privs = areas.adminPrivs, privs = areas.adminPrivs,
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
return false, "A regular expression is required." return false, S("A regular expression is required.")
end end
-- Check expression for validity -- Check expression for validity
@ -154,7 +153,7 @@ minetest.register_chatcommand("find_areas", {
("Test [1]: Player (0,0,0) (0,0,0)"):find(param) ("Test [1]: Player (0,0,0) (0,0,0)"):find(param)
end end
if not pcall(testRegExp) then if not pcall(testRegExp) then
return false, "Invalid regular expression." return false, S("Invalid regular expression.")
end end
local matches = {} local matches = {}
@ -167,24 +166,57 @@ minetest.register_chatcommand("find_areas", {
if #matches > 0 then if #matches > 0 then
return true, table.concat(matches, "\n") return true, table.concat(matches, "\n")
else else
return true, "No matches found." return true, S("No matches found.")
end end
end end
}) })
minetest.register_chatcommand("list_areas", { minetest.register_chatcommand("list_areas", {
description = "List your areas, or all areas if you are an admin.", params = S("[<name>]"),
description = S("List your areas. Admins can list all areas or those of a specific player."),
func = function(name, param) func = function(name, param)
local admin = minetest.check_player_privs(name, areas.adminPrivs) local admin = minetest.check_player_privs(name, areas.adminPrivs)
local admin_show_summary = admin
local owner_name = name
if admin and #param > 0 then
owner_name = param
admin_show_summary = false
end
local areaStrings = {} local areaStrings = {}
for id, area in pairs(areas.areas) do if admin_show_summary then
if admin or areas:isAreaOwner(id, name) then -- Summary per-player
table.insert(areaStrings, areas:toString(id)) local indices = {}
local counts = {} -- { [1] = name, [2] = count }, ...
for _, area in pairs(areas.areas) do
local i = indices[area.owner]
if i then
counts[i][2] = counts[i][2] + 1
else
table.insert(counts, { area.owner, 1 })
indices[area.owner] = #counts
end
end
-- Alphabatical name sorting
table.sort(counts, function (kv_a, kv_b)
return kv_a[1] < kv_b[1]
end)
-- Output
for _, kv in ipairs(counts) do
table.insert(areaStrings, S("@1 : @2 area(s)", kv[1], kv[2]))
end
else
-- Detailed list
for id, area in pairs(areas.areas) do
if areas:isAreaOwner(id, owner_name) then
table.insert(areaStrings, areas:toString(id))
end
end end
end end
if #areaStrings == 0 then if #areaStrings == 0 then
return true, "No visible areas." return true, S("No visible areas.")
end end
return true, table.concat(areaStrings, "\n") return true, table.concat(areaStrings, "\n")
end end
@ -192,152 +224,194 @@ minetest.register_chatcommand("list_areas", {
minetest.register_chatcommand("recursive_remove_areas", { minetest.register_chatcommand("recursive_remove_areas", {
params = "<id>", params = S("<ID>"),
description = "Recursively remove areas using an id", description = S("Recursively remove areas using an ID"),
func = function(name, param) func = function(name, param)
local id = tonumber(param) local id = tonumber(param)
if not id then if not id then
return false, "Invalid usage, see" return false, S("Invalid usage, see"
.." /help recursive_remove_areas" .." /help @1.", "recursive_remove_areas")
end end
if not areas:isAreaOwner(id, name) then if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist or is" return false, S("Area @1 does not exist or is"
.." not owned by you." .." not owned by you.", id)
end end
areas:remove(id, true) areas:remove(id, true)
areas:save() areas:save()
return true, "Removed area "..id.." and it's sub areas." return true, S("Removed area @1 and its sub areas.", id)
end end
}) })
minetest.register_chatcommand("remove_area", { minetest.register_chatcommand("remove_area", {
params = "<id>", params = S("<ID>"),
description = "Remove an area using an id", description = S("Remove an area using an ID"),
func = function(name, param) func = function(name, param)
local id = tonumber(param) local id = tonumber(param)
if not id then if not id then
return false, "Invalid usage, see /help remove_area" return false, S("Invalid usage, see /help @1.", "remove_area")
end end
if not areas:isAreaOwner(id, name) then if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist or" return false, S("Area @1 does not exist or"
.." is not owned by you." .." is not owned by you.", id)
end end
areas:remove(id) areas:remove(id)
areas:save() areas:save()
return true, "Removed area "..id return true, S("Removed area @1", id)
end end
}) })
minetest.register_chatcommand("change_owner", { minetest.register_chatcommand("change_owner", {
params = "<ID> <NewOwner>", params = S("<ID>").." "..S("<NewOwner>"),
description = "Change the owner of an area using it's ID", description = S("Change the owner of an area using its ID"),
func = function(name, param) func = function(name, param)
local id, newOwner = param:match("^(%d+)%s(%S+)$") local id, newOwner = param:match("^(%d+)%s(%S+)$")
if not id then if not id then
return false, "Invalid usage, see" return false, S("Invalid usage, see"
.." /help change_owner." .." /help @1.", "change_owner")
end end
if not areas:player_exists(newOwner) then if not areas:player_exists(newOwner) then
return false, "The player \""..newOwner return false, S("The player \"@1\" does not exist.", newOwner)
.."\" does not exist."
end end
id = tonumber(id) id = tonumber(id)
if not areas:isAreaOwner(id, name) then if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist" return false, S("Area @1 does not exist"
.." or is not owned by you." .." or is not owned by you.", id)
end end
areas.areas[id].owner = newOwner areas.areas[id].owner = newOwner
areas:save() areas:save()
minetest.chat_send_player(newOwner, minetest.chat_send_player(newOwner,
("%s has given you control over the area %q (ID %d).") S("@1 has given you control over the area \"@2\" (ID @3).",
:format(name, areas.areas[id].name, id)) name, areas.areas[id].name, id))
return true, "Owner changed." return true, S("Owner changed.")
end end
}) })
minetest.register_chatcommand("area_open", { minetest.register_chatcommand("area_open", {
params = "<ID>", params = S("<ID>"),
description = "Toggle an area open (anyone can interact) or closed", description = S("Toggle an area open (anyone can interact) or closed"),
func = function(name, param) func = function(name, param)
local id = tonumber(param) local id = tonumber(param)
if not id then if not id then
return false, "Invalid usage, see /help area_open." return false, S("Invalid usage, see /help @1.", "area_open")
end end
if not areas:isAreaOwner(id, name) then if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist" return false, S("Area @1 does not exist"
.." or is not owned by you." .." or is not owned by you.", id)
end end
local open = not areas.areas[id].open local open = not areas.areas[id].open
-- Save false as nil to avoid inflating the DB. -- Save false as nil to avoid inflating the DB.
areas.areas[id].open = open or nil areas.areas[id].open = open or nil
areas:save() areas:save()
return true, ("Area %s."):format(open and "opened" or "closed") return true, open and S("Area opened.") or S("Area closed.")
end end
}) })
minetest.register_chatcommand("area_openfarming", { minetest.register_chatcommand(
params = "<ID>", "area_openfarming", {
description = "Toggle an area open (anyone can interact farming) or closed", params = "<ID>",
func = function(name, param) description = "Toggle an area as open farming (anyone can harvest and plant) or closed",
local id = tonumber(param) func = function(name, param)
if not id then local id = tonumber(param)
return false, "Invalid usage, see /help area_openfarming." if not id then
end return false, "Invalid usage, see /help area_openfarming."
end
if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist" if not areas:isAreaOwner(id, name) then
return false, "Area "..id.." does not exist"
.." or is not owned by you." .." or is not owned by you."
end
local open = not areas.areas[id].openfarming
-- Save false as nil to avoid inflating the DB.
areas.areas[id].openfarming = open or nil
areas:save()
return true, ("Area %s to farming."):format(open and "opened" or "closed")
end end
local openfarming = not areas.areas[id].openfarming
-- Save false as nil to avoid inflating the DB.
areas.areas[id].openfarming = openfarming or nil
areas:save()
return true, ("Area %s to farming."):format(openfarming and "opened" or "closed")
end
}) })
if areas.factions_available then
minetest.register_chatcommand("area_faction_open", {
params = S("<ID> [faction_name]"),
description = S("Toggle an area open/closed for members in your faction."),
func = function(name, param)
local params = param:split(" ")
local id = tonumber(params[1])
local faction_name = params[2]
if not id or not faction_name then
return false, S("Invalid usage, see /help @1.", "area_faction_open")
end
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist"
.." or is not owned by you.", id)
end
if not factions.get_owner(faction_name) then
return false, S("Faction doesn't exists")
end
local fnames = areas.areas[id].faction_open or {}
local pos = table.indexof(fnames, faction_name)
if pos < 0 then
-- Add new faction to the list
table.insert(fnames, faction_name)
else
table.remove(fnames, pos)
end
if #fnames == 0 then
-- Save {} as nil to avoid inflating the DB.
fnames = nil
end
areas.areas[id].faction_open = fnames
areas:save()
return true, fnames and S("Area is open for members of: @1", table.concat(fnames, ", "))
or S("Area closed for faction members.")
end
})
end
minetest.register_chatcommand("move_area", { minetest.register_chatcommand("move_area", {
params = "<ID>", params = S("<ID>"),
description = "Move (or resize) an area to the current positions.", description = S("Move (or resize) an area to the current positions."),
privs = areas.adminPrivs, privs = areas.adminPrivs,
func = function(name, param) func = function(name, param)
local id = tonumber(param) local id = tonumber(param)
if not id then if not id then
return false, "Invalid usage, see /help move_area." return false, S("Invalid usage, see /help @1.", "move_area")
end end
local area = areas.areas[id] local area = areas.areas[id]
if not area then if not area then
return false, "Area does not exist." return false, S("Area does not exist.")
end end
local pos1, pos2 = areas:getPos(name) local pos1, pos2 = areas:getPos(name)
if not pos1 then if not pos1 then
return false, "You need to select an area first." return false, S("You need to select an area first.")
end end
areas:move(id, area, pos1, pos2) areas:move(id, area, pos1, pos2)
areas:save() areas:save()
return true, "Area successfully moved." return true, S("Area successfully moved.")
end, end,
}) })
minetest.register_chatcommand("area_info", { minetest.register_chatcommand("area_info", {
description = "Get information about area configuration and usage.", description = S("Get information about area configuration and usage."),
func = function(name, param) func = function(name, param)
local lines = {} local lines = {}
local privs = minetest.get_player_privs(name) local privs = minetest.get_player_privs(name)
@ -359,27 +433,24 @@ minetest.register_chatcommand("area_info", {
local max_size = has_high_limit and local max_size = has_high_limit and
size_limit_high or size_limit size_limit_high or size_limit
-- Privilege information -- Self protection information
local self_prot_line = ("Self protection is %sabled"):format( local self_prot_line = self_prot and S("Self protection is enabled.") or
self_prot and "en" or "dis") S("Self protection is disabled.")
if self_prot and prot_priv then
self_prot_line = self_prot_line..
(" %s have the neccessary privilege (%q).")
:format(
has_prot_priv and "and you" or
"but you don't",
prot_priv)
else
self_prot_line = self_prot_line.."."
end
table.insert(lines, self_prot_line) table.insert(lines, self_prot_line)
-- Privilege information
local priv_line = has_prot_priv and
S("You have the necessary privilege (\"@1\").", prot_priv) or
S("You don't have the necessary privilege (\"@1\").", prot_priv)
table.insert(lines, priv_line)
if privs.areas then if privs.areas then
table.insert(lines, "You are an area".. table.insert(lines, S("You are an area"..
" administrator (\"areas\" privilege).") " administrator (\"areas\" privilege)."))
elseif has_high_limit then elseif has_high_limit then
table.insert(lines, table.insert(lines,
"You have extended area protection".. S("You have extended area protection"..
" limits (\"areas_high_limit\" privilege).") " limits (\"areas_high_limit\" privilege)."))
elseif privs.megabuilder then
table.insert(lines, "You are a megabuilder (\"megabuilder\" privilege).")
end end
-- Area count -- Area count
@ -389,26 +460,23 @@ minetest.register_chatcommand("area_info", {
area_num = area_num + 1 area_num = area_num + 1
end end
end end
local count_line = ("You have %d area%s"):format( table.insert(lines, S("You have @1 areas.", area_num))
area_num, area_num == 1 and "" or "s")
if privs.areas then -- Area limit
count_line = count_line.. local area_limit_line = (privs.areas or privs.megabuilder) and
" and have no area protection limits." S("Limit: no area count limit") or
elseif can_prot then S("Limit: @1 areas", max_count)
count_line = count_line..(", out of a maximum of %d.") table.insert(lines, area_limit_line)
:format(max_count)
end
table.insert(lines, count_line)
-- Area size limits -- Area size limits
local function size_info(str, size) local function size_info(str, size)
table.insert(lines, ("%s spanning up to %dx%dx%d.") table.insert(lines, S("@1 spanning up to @2x@3x@4.",
:format(str, size.x, size.y, size.z)) str, size.x, size.y, size.z))
end end
local function priv_limit_info(priv, max_count, max_size) local function priv_limit_info(lpriv, lmax_count, lmax_size)
size_info(("Players with the %q privilege".. size_info(S("Players with the \"@1\" privilege"..
" can protect up to %d areas"):format( " can protect up to @2 areas", lpriv, lmax_count),
priv, max_count), max_size) lmax_size)
end end
if self_prot then if self_prot then
if privs.areas then if privs.areas then
@ -416,8 +484,11 @@ minetest.register_chatcommand("area_info", {
limit, size_limit) limit, size_limit)
priv_limit_info("areas_high_limit", priv_limit_info("areas_high_limit",
limit_high, size_limit_high) limit_high, size_limit_high)
table.insert(lines, "Players with the \"megabuilder\" privilege can protect unlimited areas in size and number.")
elseif privs.megabuilder then
table.insert(lines, "You can protect areas unlimited in size and number.")
elseif has_prot_priv then elseif has_prot_priv then
size_info("You can protect areas", max_size) size_info(S("You can protect areas"), max_size)
end end
end end
@ -426,62 +497,26 @@ minetest.register_chatcommand("area_info", {
}) })
--MFF DEBUT crabman(17/09/2015 ) respawn player at in special area(event) if a spawn is set. minetest.register_chatcommand("areas_cleanup", {
minetest.register_chatcommand("area_addspawn", { description = S("Removes all ownerless areas"),
params = "<ID>", privs = areas.adminPrivs,
privs = areas.adminPrivs, func = function()
description = "Define special spawn for area", local total, count = 0, 0
func = function(name, param)
local id = param:match("^(%d+)")
if not id then
return false, "Invalid usage, see /help area_addspawn."
end
id = tonumber(id) local aareas = areas.areas
if not id then for id, _ in pairs(aareas) do
return false, "Error, Param id must be int." local owner = aareas[id].owner
end
local player = minetest.get_player_by_name(name) if not areas:player_exists(owner) then
if not player then areas:remove(id)
return false, "Error, there is not player" count = count + 1
end end
local pos = player:getpos()
if not pos then
return false, "Error, there is not pos."
end
if not areas.areas[id] then total = total + 1
return false, "Area ".. id .." does not exist."
end end
areas.areas[id].spawn = pos
areas:save() areas:save()
return true, "spawn of area ".. id .." defined."
return true, "Total areas: " .. total .. ", Removed " ..
count .. " areas. New count: " .. (total - count)
end end
}) })
minetest.register_chatcommand("area_delspawn", {
params = "<ID>",
privs = areas.adminPrivs,
description = "Delete special spawn of area",
func = function(name, param)
local id = param:match("^(%d+)")
if not id then
return false, "Invalid usage, see /help area_delspawn."
end
id = tonumber(id)
if not id then
return false, "Error, Param id must be int."
end
if not areas.areas[id] then
return false, "Area ".. id .." does not exist."
end
areas.areas[id].spawn = nil
areas:save()
return true, "spawn of area ".. id .." deleted."
end
})
-- FIN

128
hud.lua
View File

@ -1,85 +1,89 @@
-- This is inspired by the landrush mod by Bremaweb -- This is inspired by the landrush mod by Bremaweb
local S = minetest.get_translator("areas")
areas.hud = {} areas.hud = {}
areas.hud.refresh = 0
minetest.register_globalstep(function(dtime)
areas.hud.refresh = areas.hud.refresh + dtime
if areas.hud.refresh > areas.config["tick"] then
areas.hud.refresh = 0
else
return
end
local function tick()
for _, player in pairs(minetest.get_connected_players()) do for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name() local name = player:get_player_name()
local pos = vector.round(player:getpos()) local pos = vector.round(player:get_pos())
local area_text = "No area(s)\n\n" pos = vector.apply(pos, function(p)
local area_owner_name = "" return math.max(math.min(p, 2147483), -2147483)
local mod_owner = 0 end)
local mod_open = 0 local areaStrings = {}
local mod_farming = 0
local area_name = ""
local nb_areas = 0
for id, area in pairs(areas:getAreasAtPos(pos)) do for id, area in pairs(areas:getAreasAtPos(pos)) do
nb_areas = nb_areas+1 local faction_info
if areas:isAreaOwner(id, name) then if area.faction_open and areas.factions_available then
mod_owner = 1 -- Gather and clean up disbanded factions
local changed = false
for i, fac_name in ipairs(area.faction_open) do
if not factions.get_owner(fac_name) then
table.remove(area.faction_open, i)
changed = true
end
end
if #area.faction_open == 0 then
-- Prevent DB clutter, remove value
area.faction_open = nil
else
faction_info = table.concat(area.faction_open, ", ")
end
if changed then
areas:save()
end
end end
if area.open then table.insert(areaStrings, ("%s [%u] (%s%s%s)")
mod_open = 1 :format(area.name, id, area.owner,
end area.open and S(":open") or area.openfarming and ":openfarming" or "",
if area.openfarming then faction_info and ":"..faction_info or ""))
mod_farming = 1
end
if not area.parent then
area_owner_name = area.owner
area_name = area.name
end
end end
local icon = "areas_not_area.png" for i, area in pairs(areas:getExternalHudEntries(pos)) do
if nb_areas > 0 then local str = ""
local plural = "" if area.name then str = area.name .. " " end
if nb_areas > 1 then if area.id then str = str.."["..area.id.."] " end
plural = "s" if area.owner then str = str.."("..area.owner..")" end
end table.insert(areaStrings, str)
area_text = ("%s\nOwner: %s\n%u area" .. plural):format(area_name, area_owner_name, nb_areas)
icon = ("areas_%u_%u_%u.png"):format(mod_owner, mod_open, mod_farming)
end end
if not areas.hud[name] then
areas.hud[name] = {}
areas.hud[name].icon = player:hud_add({
hud_elem_type = "image",
position = {x=0,y=1},
scale = {x=1,y=1},
offset = {x=26,y=-60},
text = icon,
})
areas.hud[name].areas_id = player:hud_add({ local areaString = S("Areas:")
hud_elem_type = "text", if #areaStrings > 0 then
areaString = areaString.."\n"..
table.concat(areaStrings, "\n")
end
local hud = areas.hud[name]
if not hud then
hud = {}
areas.hud[name] = hud
hud.areasId = player:hud_add({
[minetest.features.hud_def_type_field and "type" or "hud_elem_type"] = "text", -- compatible with older versions
name = "Areas", name = "Areas",
number = 0xFFFFFF, number = 0xFFFFFF,
position = {x=0, y=1}, position = {x=0, y=1},
offset = {x=48, y=-40}, offset = {x=8, y=-8},
text = area_text, text = areaString,
scale = {x=1, y=1}, scale = {x=200, y=60},
alignment = {x=1, y=-1}, alignment = {x=1, y=-1},
}) })
areas.hud[name].old_area_text = area_text hud.oldAreas = areaString
areas.hud[name].old_icon = icon return
else elseif hud.oldAreas ~= areaString then
if areas.hud[name].old_area_text ~= area_text then player:hud_change(hud.areasId, "text", areaString)
player:hud_change(areas.hud[name].areas_id, "text", area_text) hud.oldAreas = areaString
areas.hud[name].old_area_text = area_text
end
if areas.hud[name].old_icon ~= icon then
player:hud_change(areas.hud[name].icon, "text", icon)
areas.hud[name].old_icon = icon
end
end end
end end
minetest.after(1.5, tick) end)
end
tick()
minetest.register_on_leaveplayer(function(player) minetest.register_on_leaveplayer(function(player)
areas.hud[player:get_player_name()] = nil areas.hud[player:get_player_name()] = nil
end) end)

View File

@ -4,6 +4,8 @@
areas = {} areas = {}
areas.factions_available = minetest.get_modpath("playerfactions") and true
areas.adminPrivs = {areas=true} areas.adminPrivs = {areas=true}
areas.startTime = os.clock() areas.startTime = os.clock()
@ -19,21 +21,28 @@ dofile(areas.modpath.."/hud.lua")
areas:load() areas:load()
local S = minetest.get_translator("areas")
minetest.register_privilege("areas", { minetest.register_privilege("areas", {
description = "Can administer areas." description = S("Can administer areas."),
give_to_singleplayer = false
}) })
minetest.register_privilege("areas_high_limit", { minetest.register_privilege("areas_high_limit", {
description = "Can can more, bigger areas." description = S("Can protect more, bigger areas."),
give_to_singleplayer = false
})
-- Mega_builder privilege -- MFF
minetest.register_privilege("megabuilder", {
description = "Can protect an infinite amount of areas."
}) })
if not minetest.registered_privileges[areas.config.self_protection_privilege] then if not minetest.registered_privileges[areas.config.self_protection_privilege] then
minetest.register_privilege(areas.config.self_protection_privilege, { minetest.register_privilege(areas.config.self_protection_privilege, {
description = "Can protect areas.", description = S("Can protect areas."),
}) })
end end
if minetest.setting_getbool("log_mod") then if minetest.settings:get_bool("log_mods") then
local diffTime = os.clock() - areas.startTime local diffTime = os.clock() - areas.startTime
minetest.log("action", "areas loaded in "..diffTime.."s.") minetest.log("action", "areas loaded in "..diffTime.."s.")
end end

View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("areas")
local old_is_protected = minetest.is_protected local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name) function minetest.is_protected(pos, name)
@ -11,9 +12,8 @@ minetest.register_on_protection_violation(function(pos, name)
if not areas:canInteract(pos, name) then if not areas:canInteract(pos, name) then
local owners = areas:getNodeOwners(pos) local owners = areas:getNodeOwners(pos)
minetest.chat_send_player(name, minetest.chat_send_player(name,
("%s is protected by %s."):format( S("@1 is protected by @2.",
minetest.pos_to_string(pos), minetest.pos_to_string(pos),
table.concat(owners, ", "))) table.concat(owners, ", ")))
end end
end) end)

View File

@ -1,23 +1,29 @@
-- Mega_builder privilege local S = minetest.get_translator("areas")
minetest.register_privilege("megabuilder","Can protect an infinite amount of areas.")
function areas:player_exists(name) function areas:player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil return minetest.get_auth_handler().get_auth(name) ~= nil
end end
local safe_file_write = minetest.safe_file_write
if safe_file_write == nil then
function safe_file_write(path, content)
local file, err = io.open(path, "w")
if err then
return err
end
file:write(content)
file:close()
end
end
-- Save the areas table to a file -- Save the areas table to a file
function areas:save() function areas:save()
local datastr = minetest.serialize(self.areas) local datastr = minetest.write_json(self.areas)
if not datastr then if not datastr then
minetest.log("error", "[areas] Failed to serialize area data!") minetest.log("error", "[areas] Failed to serialize area data!")
return return
end end
local file, err = io.open(self.config.filename, "w") return safe_file_write(self.config.filename, datastr)
if err then
return err
end
file:write(datastr)
file:close()
end end
-- Load the areas table from the save file -- Load the areas table from the save file
@ -27,10 +33,19 @@ function areas:load()
self.areas = self.areas or {} self.areas = self.areas or {}
return err return err
end end
self.areas = minetest.deserialize(file:read("*a")) local data = file:read("*a")
if data:sub(1, 1) == "[" then
self.areas, err = minetest.parse_json(data)
else
self.areas, err = minetest.deserialize(data)
end
if type(self.areas) ~= "table" then if type(self.areas) ~= "table" then
self.areas = {} self.areas = {}
end end
if err and #data > 10 then
minetest.log("error", "[areas] Failed to load area data: " ..
tostring(err))
end
file:close() file:close()
self:populateStore() self:populateStore()
end end
@ -42,7 +57,7 @@ end
function areas:checkAreaStoreId(sid) function areas:checkAreaStoreId(sid)
if not sid then if not sid then
minetest.log("error", "AreaStore failed to find an ID for an " minetest.log("error", "AreaStore failed to find an ID for an "
.."area! Falling back to iterative area checking.") .."area! Falling back to iterative area checking.")
self.store = nil self.store = nil
self.store_ids = nil self.store_ids = nil
end end
@ -88,6 +103,11 @@ function areas:add(owner, name, pos1, pos2, parent)
owner = owner, owner = owner,
parent = parent parent = parent
} }
for i=1, #areas.registered_on_adds do
areas.registered_on_adds[i](id, self.areas[id])
end
-- Add to AreaStore -- Add to AreaStore
if self.store then if self.store then
local sid = self.store:insert_area(pos1, pos2, tostring(id)) local sid = self.store:insert_area(pos1, pos2, tostring(id))
@ -98,7 +118,7 @@ function areas:add(owner, name, pos1, pos2, parent)
return id return id
end end
--- Remove a area, and optionally it's children recursively. --- Remove a area, and optionally its 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)
@ -120,6 +140,10 @@ function areas:remove(id, recurse)
end end
end end
for i=1, #areas.registered_on_removes do
areas.registered_on_removes[i](id)
end
-- Remove main entry -- Remove main entry
self.areas[id] = nil self.areas[id] = nil
@ -135,6 +159,10 @@ function areas:move(id, area, pos1, pos2)
area.pos1 = pos1 area.pos1 = pos1
area.pos2 = pos2 area.pos2 = pos2
for i=1, #areas.registered_on_moves do
areas.registered_on_moves[i](id, area, pos1, pos2)
end
if self.store then if self.store then
self.store:remove_area(areas.store_ids[id]) self.store:remove_area(areas.store_ids[id])
local sid = self.store:insert_area(pos1, pos2, tostring(id)) local sid = self.store:insert_area(pos1, pos2, tostring(id))
@ -167,7 +195,7 @@ function areas:isSubarea(pos1, pos2, id)
end end
end end
-- Returns a table (list) of children of an area given it's identifier -- Returns a table (list) of children of an area given its identifier
function areas:getChildren(id) function areas:getChildren(id)
local children = {} local children = {}
for cid, area in pairs(self.areas) do for cid, area in pairs(self.areas) do
@ -178,64 +206,93 @@ function areas:getChildren(id)
return children return children
end end
-- Checks if the user has sufficient privileges. -- checks all possible restrictions registered with
-- If the player is not a administrator it also checks -- areas:registerProtectionCondition
-- if the area intersects other areas that they do not own. -- builtin callbacks below
-- Also checks the size of the area and if the user already
-- has more than max_areas.
function areas:canPlayerAddArea(pos1, pos2, name) function areas:canPlayerAddArea(pos1, pos2, name)
local allowed = true
local errMsg
for i=1, #areas.registered_protection_conditions do
local res, msg = areas.registered_protection_conditions[i](pos1, pos2, name)
if res == true then
-- always allow to protect, no matter of other conditions
return true
elseif res == false then
-- there might be another callback that returns true, so we can't break here
allowed = false
-- save the first error that occurred
errMsg = errMsg or msg
elseif res ~= nil then
local origin = areas.callback_origins[areas.registered_protection_conditions[i]]
error("\n[Mod] areas: Invalid api usage from mod '" ..
origin.mod .. "' in callback registerProtectionCondition() at " ..
origin.source .. ":" .. origin.line)
end
end
return allowed, errMsg
end
-- Checks if the user has sufficient privileges.
areas:registerProtectionCondition(function(pos1, pos2, name)
local privs = minetest.get_player_privs(name) local privs = minetest.get_player_privs(name)
if privs.areas then if privs.areas then
-- always allow administrators to create areas
return true return true
end end
-- Check self protection privilege, if it is enabled, -- Check self protection privilege
-- and if the area is too big. if not areas.config.self_protection or
if not self.config.self_protection or
not privs[areas.config.self_protection_privilege] then not privs[areas.config.self_protection_privilege] then
return false, "Self protection is disabled or you do not have" return false, S("Self protection is disabled or you do not have"
.." the necessary privilege." .." the necessary privilege.")
end end
end)
-- MFF: megabuilders skip checks on size and number of areas. -- check if the area is too big
if not privs["megabuilder"] then -- NALC: megabuilders skip checks on size and number of areas
-- Check size areas:registerProtectionCondition(function(pos1, pos2, name)
local privs = minetest.get_player_privs(name)
if not privs.megabuilder then
local max_size = privs.areas_high_limit and local max_size = privs.areas_high_limit and
self.config.self_protection_max_size_high or areas.config.self_protection_max_size_high or
self.config.self_protection_max_size areas.config.self_protection_max_size
if if
(pos2.x - pos1.x) > max_size.x or (pos2.x - pos1.x + 1) > max_size.x or
(pos2.y - pos1.y) > max_size.y or (pos2.y - pos1.y + 1) > max_size.y or
(pos2.z - pos1.z) > max_size.z then (pos2.z - pos1.z + 1) > max_size.z then
return false, "Area is too big." return false, S("Area is too big.")
end
-- Check number of areas the user has and make sure it not above the max
local count = 0
for _, area in pairs(self.areas) do
if area.owner == name then
count = count + 1
end
end
local max_areas = privs.areas_high_limit and
self.config.self_protection_max_areas_high or
self.config.self_protection_max_areas
if count >= max_areas then
return false, "You have reached the maximum amount of"
.." areas that you are allowed to protect."
end end
end end
end)
-- Check intersecting areas -- Check number of areas the user has and make sure it not above the max
local can, id = self:canMakeArea(pos1, pos2, name) --MFF crabman(25/02/2016) fix areas in areas areas:registerProtectionCondition(function(pos1, pos2, name)
local privs = minetest.get_player_privs(name)
local count = 0
for _, area in pairs(areas.areas) do
if area.owner == name then
count = count + 1
end
end
local max_areas = privs.areas_high_limit and
areas.config.self_protection_max_areas_high or
areas.config.self_protection_max_areas
if count >= max_areas then
return false, S("You have reached the maximum amount of"
.." areas that you are allowed to protect.")
end
end)
-- checks if the area intersects other areas that the player do not own.
areas:registerProtectionCondition(function(pos1, pos2, name)
local can, id = areas:canInteractInArea(pos1, pos2, name)
if not can then if not can then
local area = self.areas[id] local area = areas.areas[id]
return false, ("The area intersects with %s [%u] (%s).") return false, S("The area intersects with @1 [@2] (@3).",
:format(area.name, id, area.owner) area.name, id, area.owner)
end end
end)
return true
end
-- Given a id returns a string in the format: -- Given a id returns a string in the format:
-- "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children" -- "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children"
@ -288,4 +345,3 @@ function areas:isAreaOwner(id, name)
end end
return false return false
end end

View File

@ -1,25 +1,26 @@
-- This file contains functions to convert from -- This file contains functions to convert from
-- the old areas format and other compatability code. -- the old areas format and other compatability code.
local S = minetest.get_translator("areas")
minetest.register_chatcommand("legacy_load_areas", { minetest.register_chatcommand("legacy_load_areas", {
params = "<version>", params = S("<version>"),
description = "Loads, converts, and saves the areas from" description = S("Loads, converts, and saves the areas from"
.." a legacy save file.", .." a legacy save file."),
privs = {areas=true, server=true}, privs = {areas=true, server=true},
func = function(name, param) func = function(name, param)
minetest.chat_send_player(name, "Converting areas...") minetest.chat_send_player(name, S("Converting areas…"))
local version = tonumber(param) local version = tonumber(param)
if version == 0 then if version == 0 then
err = areas:node_ownership_load() local err = areas:node_ownership_load()
if err then if err then
minetest.chat_send_player(name, "Error loading legacy file: "..err) minetest.chat_send_player(name, S("Error loading legacy file: @1", err))
return return
end end
else else
minetest.chat_send_player(name, "Invalid version number. (0 allowed)") minetest.chat_send_player(name, S("Invalid version number. (0 allowed)"))
return return
end end
minetest.chat_send_player(name, "Legacy file loaded.") minetest.chat_send_player(name, S("Legacy file loaded."))
for k, area in pairs(areas.areas) do for k, area in pairs(areas.areas) do
-- New position format -- New position format
@ -34,20 +35,21 @@ minetest.register_chatcommand("legacy_load_areas", {
areas:sortPos(area.pos1, area.pos2) areas:sortPos(area.pos1, area.pos2)
-- Add name -- Add name
area.name = "unnamed" area.name = S("unnamed")
-- Remove ID -- Remove ID
area.id = nil area.id = nil
end end
minetest.chat_send_player(name, "Table format updated.") minetest.chat_send_player(name, S("Table format updated."))
areas:save() areas:save()
minetest.chat_send_player(name, "Converted areas saved. Done.") minetest.chat_send_player(name, S("Converted areas saved. Done."))
end end
}) })
function areas:node_ownership_load() function areas:node_ownership_load()
local filename = minetest.get_worldpath().."/owners.tbl" local filename = minetest.get_worldpath().."/owners.tbl"
local tables, err
tables, err = loadfile(filename) tables, err = loadfile(filename)
if err then if err then
return err return err
@ -104,35 +106,3 @@ function areas.hasOwner(pos)
end end
return false return false
end end
IsPlayerNodeOwner = areas.isNodeOwner
GetNodeOwnerName = areas.getNodeOwnerName
HasOwner = areas.hasOwner
-- This is entirely untested and may break in strange and new ways.
if areas.config.legacy_table then
owner_defs = setmetatable({}, {
__index = function(table, key)
local a = rawget(areas.areas, key)
if not a then return a end
local b = {}
for k, v in pairs(a) do b[k] = v end
b.x1, b.y1, b.z1 = b.pos1.x, b.pos1.y, b.pos1.z
b.x2, b.y1, b.z2 = b.pos2.x, b.pos2.y, b.pos2.z
b.pos1, b.pos2 = nil, nil
b.id = key
return b
end,
__newindex = function(table, key, value)
local a = value
a.pos1, a.pos2 = {x=a.x1, y=a.y1, z=a.z1},
{x=a.x2, y=a.y2, 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"
a.id = nil
return rawset(areas.areas, key, a)
end
})
end

137
locale/areas.fr.tr Normal file
View File

@ -0,0 +1,137 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<NomZone>
<ID> [faction_name]=<ID> [nom_de_faction]
<NewOwner>=<NouveauPropriétaire>
<ParentID>=<IDZonePrincipale>
<PlayerName>=<NomJoueur>
<newName>=<NouveauNom>
@1 has given you control over the area "@2" (ID @3).=@1 vous a donné le contrôle de la zone "@2" (ID @3).
@1 spanning up to @2x@3x@4.=@1 sétendant jusquà @2x@3x@4.
A regular expression is required.=Une expression régulière est requise.
Area @1 does not exist or is not owned by you.=La zone @1 nexiste pas ou ne vous appartient pas.
Area closed for faction members.=Zone fermée aux membres de la faction.
Area closed.=Zone fermée.
Area does not exist.=La zone nexiste pas.
Area is open for members of: @1=Zone ouverte aux membres de ces factions : @1
Area opened.=Zone ouverte.
Area protected. ID: @1=Zone protégée. ID : @1
Area renamed.=Zone renommée.
Area successfully moved.=Zone déplacée avec succès.
Change the owner of an area using its ID=Change le propriétaire dune zone en utilisant son ID.
Faction doesn't exists=La faction n'existe pas
Find areas using a Lua regular expression=Trouve les zones en utilisant une expression régulière Lua.
Get information about area configuration and usage.=Obtient des informations sur la configuration des zones et lutilisation des zones.
Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Donne au joueur accès aux sous-zones entre deux positions qui ont déjà été protégées ; utilisez set_owner si vous ne voulez pas que la zone pricipale soit définie.
Invalid regular expression.=Expression régulière invalide.
Limit: @1 areas=Limite: @1 zones.
Limit: no area count limit=Limite: pas de limite de nombre de zones.
List your areas, or all areas if you are an admin.=Liste vos zones, ou toutes les zones si vous êtes administrateur.
Move (or resize) an area to the current positions.=Déplace (ou redimensionne) une zone aux positions actuelles.
No matches found.=Aucun résultat.
No visible areas.=Pas de zone visible.
Owner changed.=Propriétaire changé.
Players with the "@1" privilege can protect up to @2 areas=Les joueurs avec le privilège "@1" peuvent protéger jusquà @2 zones
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Protège une zone entre deux positions et donne à un joueur accès à cette zone sans définir la zone principale de cette zone ni aucune zone existante.
Protect your own area=Protège votre zone.
Recursively remove areas using an ID=Supprime les zones récursivement en utilisant un ID.
Remove an area using an ID=Supprime une zone en utilisant son ID.
Removed area @1=Zone @1 supprimée.
Removed area @1 and its sub areas.=Zone @1 et ses sous-zones supprimées.
Removes all ownerless areas=Supprime toutes les zones sans propriétaire
Rename an area that you own=Renomme une zone qui vous appartient.
Self protection is disabled.=Lautoprotection est désactivée.
Self protection is enabled.=Lautoprotection est activée.
That area doesn't exist.=La zone nexiste pas.
The player "@1" does not exist.=Le joueur "@1" nexiste pas.
Toggle an area open (anyone can interact) or closed=Bascule entre zone ouverte (tout le monde peut intéragir) ou fermée.
Toggle an area open/closed for members in your faction.=Bascule entre zone ouverte/fermée pour les membres de votre faction.
You are an area administrator ("areas" privilege).=Vous êtes un administrateur de zone (privilège "areas").
You can protect areas=Vous pouvez protéger des zones.
You can't protect that area.=Vous ne pouvez pas protéger cette zone.
You can't protect that area: @1=Vous ne pouvez pas protéger cette zone : @1.
You don't have the necessary privilege ("@1").=Vous navez pas le privilège nécessaire ("@1").
You don't own that area.=Vous ne possédez pas cette zone.
You have @1 areas.=Vous avez @1 zones.
You have been granted control over area #@1. Type /list_areas to show your areas.=Vous avez reçu lautorisation de contrôler la zone #@1.
You have extended area protection limits ("areas_high_limit" privilege).=Votre limite de protection de zones est étendue (privilège "areas_high_limit").
You have the necessary privilege ("@1").=Vous avez le privilège nécessaire ("@1").
You need to select an area first.=Vous devez sélectionner une zone dabord.
### chatcommands.lua ###
### pos.lua ###
<ID>=<ID>
Invalid usage, see /help @1.=Utilisation incorrecte, voir /help @1.
### hud.lua ###
:open= : ouverte
Areas:=Zones :
### init.lua ###
Can administer areas.=Permet dadministrer des zones.
Can protect areas.=Permet de protéger des zones.
Can protect more, bigger areas.=Permet de protéger plus, et de plus grandes zones.
### interact.lua ###
@1 is protected by @2.=@1 est protégée par @2.
### internal.lua ###
Area is too big.=La zone est trop grande.
Self protection is disabled or you do not have the necessary privilege.=Lautoprotection est désactivée ou vous navez pas le privilège nécessaire.
The area intersects with @1 [@2] (@3).=La zone a une intersection avec @1 [@2] (@3).
You have reached the maximum amount of areas that you are allowed to protect.=Vous avez atteint le nombre maximum de zones que vous êtes autorisé à protéger.
### legacy.lua ###
<version>=<version>
Converted areas saved. Done.=Zones converties sauvegardées. Fait.
Converting areas…=Conversion des zones…
Error loading legacy file: @1=Erreur lors du chargement du fichier : @1
Invalid version number. (0 allowed)=Numéro de version invalide. (0 autorisé)
Legacy file loaded.=Fichier obsolète chargé.
Loads, converts, and saves the areas from a legacy save file.=Charge, fait la conversion et sauvegarde les zones depuis un fichier de sauvegarde obsolète.
Table format updated.=Format de tableau mis à jour.
unnamed=Non nommé
### pos.lua ###
<not set>=<no définie>
Area @1 selected.=Zone @1 sélectionnée.
Area position @1 set to @2=Position @1 de la zone définie à @2.
Position @1 set to @2=Position @1 définie à @2.
Position @1: =Position @1 :
Select an area by ID.=Sélectionnez une zone par son ID.
Select position @1 by punching a node.=Sélectionnez une position @1 en frappant un bloc.
Select positions by punching two nodes.=Sélectionnez une position en frappant deux blocs.
Set area protection region position @1 to your location or the one specified=Définit la position @1 de la région de protection de zone à votre position ou à celle spécifiée.
Set area protection region, position 1, or position 2 by punching nodes, or display the region=Définit la région de protection de zone, la position 1, ou la position 2 en frappant des blocs, ou en affichant la région.
The area @1 does not exist.=La zone @1 nexiste pas.
Unable to get position.=Impossible dobtenir la position.
Unknown subcommand: @1=Sous-commande inconnue : @1
Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.=

137
locale/areas.it.tr Normal file
View File

@ -0,0 +1,137 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<NomeArea>
<ID> [faction_name]= <ID> [nome_fazione]
<NewOwner>=<NuovoProprietario>
<ParentID>=<IDparent>
<PlayerName>=<NomeGiocatore>
<newName>=<nuovoNome>
@1 has given you control over the area "@2" (ID @3).=@1 ti ha dato il controllo sull'area "@2" (ID @3).
@1 spanning up to @2x@3x@4.=@1 si estende fino a @2x@3@4.
A regular expression is required.=È necessaria una espressione regolare.
Area @1 does not exist or is not owned by you.=L'area @1 non esiste o non è di tua proprietà.
Area closed for faction members.=Area chiusa per i membri della fazione.
Area closed.=Area chiusa.
Area does not exist.=L'area non esiste.
Area is open for members of: @1=L'area è aperta ai membri di: @1
Area opened.=Area aperta.
Area protected. ID: @1=Area protetta. ID: @1
Area renamed.=Area rinominata.
Area successfully moved.=Area spostata con successo.
Change the owner of an area using its ID=Cambia il proprietario di un'area usando il suo ID
Faction doesn't exists=La fazione non esiste
Find areas using a Lua regular expression=Trova aree usando una espressione regolare Lua
Get information about area configuration and usage.=Ottieni informazioni sulla configurazione e l'uso delle aree.
Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Dai a un giocatore l'accesso a una sotto-area tra due posizioni che sono già state protette, usa set_owner se non vuoi che sia impostato il parent.
Invalid regular expression.=Espressione regolare non valida.
Limit: @1 areas=Limite: @1 aree
Limit: no area count limit=Limite: nessun limite al numero delle aree
List your areas, or all areas if you are an admin.=Elenca le tue aree, o tutte le aree se sei un amministratore.
Move (or resize) an area to the current positions.=Sposta (o ridimensiona) un'area alle posizioni attuali.
No matches found.=Nessuna corrispondenza trovata.
No visible areas.=Nessuna area visibile.
Owner changed.=Proprietario cambiato.
Players with the "@1" privilege can protect up to @2 areas=I giocatori col privilegio "@1" possono proteggere fino a @2 aree
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Proteggi un'area tra due posizioni e danne l'accesso a un giocatore senza impostare il parent dell'area a qualsiasi area esistente
Protect your own area=Proteggi la tua area
Recursively remove areas using an ID=Elimina ricorsivamente delle aree usando un ID
Remove an area using an ID=Elimina un'area usando un ID
Removed area @1=Eliminata l'area @1
Removed area @1 and its sub areas.=Eliminata l'area @1 e le sue sotto-aree.
Removes all ownerless areas=
Rename an area that you own=Rinomina un'area che ti appartiene
Self protection is disabled.=L'auto-protezione è disattivata.
Self protection is enabled.=L'auto-protezione è attivata.
That area doesn't exist.=Quell'area non esiste.
The player "@1" does not exist.=Il giocatore "@1" non esiste.
Toggle an area open (anyone can interact) or closed=Apri o chiudi un'area (chiunque può interagirvi)
Toggle an area open/closed for members in your faction.=Apri o chiudi un'area per i membri della tua fazione.
You are an area administrator ("areas" privilege).=Sei un amministratore di aree (privilegio "areas")
You can protect areas=Puoi proteggere aree
You can't protect that area.=Non puoi proteggere quell'area.
You can't protect that area: @1=Non puoi proteggere quell'area: @1
You don't have the necessary privilege ("@1").=Non hai il privilegio necessario ("@1")
You don't own that area.=Non possiedi quell'area.
You have @1 areas.=Hai @1 aree.
You have been granted control over area #@1. Type /list_areas to show your areas.=Ti è stato concesso il controllo sull'area #@1. Digita /list_areas per mostrare le tue aree.
You have extended area protection limits ("areas_high_limit" privilege).=Hai limiti di protezione aree estesi (privilegio "areas_high_limit")
You have the necessary privilege ("@1").=Hai il privilegio necessario ("@1")
You need to select an area first.=Prima devi selezionare un'area.
### chatcommands.lua ###
### pos.lua ###
<ID>=<ID>
Invalid usage, see /help @1.=Utilizzo non valido, si veda /help @1.
### hud.lua ###
:open=:aperta
Areas:=Aree:
### init.lua ###
Can administer areas.=
Can protect areas.=
Can protect more, bigger areas.=
### interact.lua ###
@1 is protected by @2.=@1 è protetta da @2.
### internal.lua ###
Area is too big.=L'area è troppo grande.
Self protection is disabled or you do not have the necessary privilege.=L'auto-protezione è disattivata o non possiedi il privilegio necessario.
The area intersects with @1 [@2] (@3).=L'area interseca con @1 [@2] (@3).
You have reached the maximum amount of areas that you are allowed to protect.=Hai raggiunto il numero massimo di aree che ti è consentito proteggere.
### legacy.lua ###
<version>=<versione>
Converted areas saved. Done.=Aree convertite salvate. Fatto.
Converting areas…=Conversione delle aree...
Error loading legacy file: @1=Errore nel caricamento del file precedente: @1
Invalid version number. (0 allowed)=Numero di versione non valido. (0 permesso)
Legacy file loaded.=File precedente caricato.
Loads, converts, and saves the areas from a legacy save file.=Carica, converte e salva le aree da un file di salvataggio precedente.
Table format updated.=Aggiornato il formato della tabella.
unnamed=innominato
### pos.lua ###
<not set>=<non impostato>
Area @1 selected.=Area @1 selezionata.
Area position @1 set to @2=Posizione @1 dell'area impostata a @2
Position @1 set to @2=Posizione @1 impostata a @2
Position @1: =Posizione @1:
Select an area by ID.=Scegli un'area tramite l'ID.
Select position @1 by punching a node.=Seleziona la posizione @1 colpendo un nodo.
Select positions by punching two nodes.=Seleziona le posizioni colpendo due nodi.
Set area protection region position @1 to your location or the one specified=Imposta la protezione area della posizione @1 della regione alla tua posizione o quella specificata
Set area protection region, position 1, or position 2 by punching nodes, or display the region=Imposta la protezione area della regione, posizione 1, o posizione 2, colpendo due nodi, o mostra la regione
The area @1 does not exist.=L'area @1 non esiste.
Unable to get position.=Impossibile ottenere la posizione.
Unknown subcommand: @1=Sotto-comando sconosciuto: @1
Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.=

137
locale/areas.ru.tr Normal file
View File

@ -0,0 +1,137 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<имя_территории>
<ID> [faction_name]=<ID> [имя_фракции]
<NewOwner>=<новый_владелец>
<ParentID>=<ID_родительской>
<PlayerName>=<имя_игрока>
<newName>=<новое_имя>
@1 has given you control over the area "@2" (ID @3).=@1 передал вам территорию "@2" (ID @3).
@1 spanning up to @2x@3x@4.=@1 размером до @2x@3x@4.
A regular expression is required.=Не указано регулярное выражение.
Area @1 does not exist or is not owned by you.=Территория @1 не существует или Вы не имеете к ней доступа.
Area closed for faction members.=Территория закрыта для игроков из фракций.
Area closed.=Территория закрыта.
Area does not exist.=Территория не существует.
Area is open for members of: @1=Территория открыта для игроков из фракций: @1
Area opened.=Территория открыта.
Area protected. ID: @1=Территория защищена. ID: @1
Area renamed.=Территория переименована.
Area successfully moved.=Территория успешно перенесена.
Change the owner of an area using its ID=Изменить владельца территории по её ID
Faction doesn't exists=Фракция не существует.
Find areas using a Lua regular expression=Поиск территорий с использованием регулярных выражений Lua
Get information about area configuration and usage.=Получить информацию о конфигурации и использовании защиты территорий.
Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Дать игроку доступ к выделенной территории, создав дополнительный регион. Используйте set_owner, если вы не хотите привязки к родительской территории.
Invalid regular expression.=Неверное регулярное выражение.
Limit: @1 areas=Ограничение: @1 территорий(я)
Limit: no area count limit=Ограничение: нет ограничений на кол-во территорий
List your areas, or all areas if you are an admin.=Вывести список доступных Вам территорий или всех существующих территорий, если вы администратор.
Move (or resize) an area to the current positions.=Переместить (или изменить размер) территории.
No matches found.=Совпадений не найдено.
No visible areas.=Нет доступных территорий.
Owner changed.=Владелец изменён.
Players with the "@1" privilege can protect up to @2 areas=Игроки, имеющие привилегию "@1" могут защищать до @2 территорий
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Защитить территорию между двумя позициями и дать игроку доступ к ней без привязки к родительскому региону.
Protect your own area=Защитить вашу территорию
Recursively remove areas using an ID=Рекурсивное удаление территорий (вместе с дочерними) по его ID
Remove an area using an ID=Удаление территории по его ID
Removed area @1=Удалена территория @1
Removed area @1 and its sub areas.=Удалена территория @1 и её дочерние территории.
Removes all ownerless areas=Удалить все бесхозные территории (без владельца)
Rename an area that you own=Переименовать территорию, которой вы владеете.
Self protection is disabled.=Защита своих территорий отключена.
Self protection is enabled.=Защита своих территорий включена.
That area doesn't exist.=Территория не существует.
The player "@1" does not exist.=Игрок "@1" не существует.
Toggle an area open (anyone can interact) or closed=Открыть/закрыть территорию для других игроков
Toggle an area open/closed for members in your faction.=Открыть/закрыть территорию для игроков вашей фракции.
You are an area administrator ("areas" privilege).=Вы можете управлять всеми территориями (привилегия "areas")
You can protect areas=Вы можете защищать территории
You can't protect that area.=Вы не можете установить защиту на эту территорию.
You can't protect that area: @1=Вы не можете установить защиту на территорию: @1
You don't have the necessary privilege ("@1").=У Вас нет необходимой привилегии ("@1").
You don't own that area.=Вы не являетесь владельцем этой территории.
You have @1 areas.=У вас @1 территорий.
You have been granted control over area #@1. Type /list_areas to show your areas.=Вам выдан доступ к территории #@1.
You have extended area protection limits ("areas_high_limit" privilege).=У вас увеличенный лимит на кол-во создаваемых вами защищённых территорий
You have the necessary privilege ("@1").=У вас есть необходимая привилегия ("@1").
You need to select an area first.=Сначала необходимо выделить территорию.
### chatcommands.lua ###
### pos.lua ###
<ID>=
Invalid usage, see /help @1.=Неверное использование, см. /help @1.
### hud.lua ###
:open=:открытая
Areas:=Территории:
### init.lua ###
Can administer areas.=Может управлять территориями.
Can protect areas.=Может создавать защищённые территории.
Can protect more, bigger areas.=Может создавать защищённые территории больше территорий и с бОльшим размером.
### interact.lua ###
@1 is protected by @2.=@1 - принадлежит @2.
### internal.lua ###
Area is too big.=Территория слишком велика.
Self protection is disabled or you do not have the necessary privilege.=Возможность защиты территорий отключена или же Вы не имеете необходимых привилегий.
The area intersects with @1 [@2] (@3).=Территория пересекается с @1 [@2] (@3).
You have reached the maximum amount of areas that you are allowed to protect.=Вы достигли максимально допустимого количества на создание защищённых территорий.
### legacy.lua ###
<version>=<версия>
Converted areas saved. Done.=Готово. Сконвертированные территории сохранены.
Converting areas…=Конвертирование территорий…
Error loading legacy file: @1=Ошибка загрузки файла с устаревшим форматом: @1
Invalid version number. (0 allowed)=Неверный номер версии. (поддерживается 0)
Legacy file loaded.=Файл с устаревшим форматом загружен.
Loads, converts, and saves the areas from a legacy save file.=Загружает, конвертирует и сохраняет территории из файла с устаревшим форматом.
Table format updated.=Формат обновлён.
unnamed=без_названия
### pos.lua ###
<not set>=<не_установлена>
Area @1 selected.=Территория @1 выбрана.
Area position @1 set to @2=Позиция @1 установлена в @2
Position @1 set to @2=Позиция @1 установлена в @2
Position @1: =Позиция @1:
Select an area by ID.=Выбрать территорию по её ID.
Select position @1 by punching a node.=Установите позицию @1, ударив по блоку.
Select positions by punching two nodes.=Установите позиции, ударив по двум блокам.
Set area protection region position @1 to your location or the one specified=Установить позицию @1 для выбора защищаемой территории
Set area protection region, position 1, or position 2 by punching nodes, or display the region=Установить позиции 1 и/или 2, ударяя по блокам, или вывести ранее установленные позиции
The area @1 does not exist.=Территория @1 не существует.
Unable to get position.=Не удалось получить позицию.
Unknown subcommand: @1=Неизвестная под-команда/аргумент.
Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.=

137
locale/areas.zh_CN.tr Normal file
View File

@ -0,0 +1,137 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<保护区名称>
<ID> [faction_name]=<保护区编号> [派系名称]
<NewOwner>=<新拥有者>
<ParentID>=<上级保护区编号>
<PlayerName>=<玩家名称>
<newName>=<新名称>
@1 has given you control over the area "@2" (ID @3).=@1 已将保护区“@2”编号 @3的控制权交与您。
@1 spanning up to @2x@3x@4.=@1最大为 @2x@3x@4。
A regular expression is required.=需要正则表达式。
Area @1 does not exist or is not owned by you.=保护区 @1 不存在,或并非由您拥有。
Area closed for faction members.=保护区已对派系成员关闭。
Area closed.=保护区已关闭。
Area does not exist.=保护区不存在。
Area is open for members of: @1=保护区已对以下派系的成员开放:@1
Area opened.=保护区已开放。
Area protected. ID: @1=成功进行保护。 保护区编号:@1
Area renamed.=保护区已重新命名。
Area successfully moved.=成功移动保护区。
Change the owner of an area using its ID=通过保护区编号改变其拥有者
Faction doesn't exists=派系不存在
Find areas using a Lua regular expression=使用 Lua 正则表达式寻找保护区
Get information about area configuration and usage.=获得保护区配置与使用的资讯。
Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=授予其他玩家已有保护区内一部分地方、两个座标之间区域的的存取权。如果不想上级保护区被设定,请使用 /set_owner。
Invalid regular expression.=正则表达式无效。
Limit: @1 areas=限制:@1个保护区
Limit: no area count limit=限制:无数量限制
List your areas, or all areas if you are an admin.=列出您拥有的保护区,或对于管理员而言,列出所有保护区。
Move (or resize) an area to the current positions.=移动一保护区至目前座标,或改变其大小。
No matches found.=找不到匹配项。
No visible areas.=没有保护区可供查阅。
Owner changed.=拥有者已修改。
Players with the "@1" privilege can protect up to @2 areas=拥有“@1”权限的玩家最多可以保护 @2 个保护区
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=授予其他玩家两个座标之间区域的控制权,而不设定上级保护区
Protect your own area=对您自己的地区施加保护
Recursively remove areas using an ID=通过保护区编号移除保护区以及其所有子保护区
Remove an area using an ID=通过保护区编号移除一保护区
Removed area @1=保护区 @1 已移除
Removed area @1 and its sub areas.=保护区 @1 以及其所有子保护区已移除。
Removes all ownerless areas=移除所有无主保护区
Rename an area that you own=重新命名您拥有的保护区
Self protection is disabled.=自我保护被禁用。
Self protection is enabled.=自我保护被启用。
That area doesn't exist.=保护区不存在。
The player "@1" does not exist.=玩家“@1”不存在。
Toggle an area open (anyone can interact) or closed=切换保护区开放状态(如开放,所有玩家均可互动)
Toggle an area open/closed for members in your faction.=切换保护区对派系成员的开放状态
You are an area administrator ("areas" privilege).=您是保护区管理员拥有“area”权限
You can protect areas=您可以建立保护区
You can't protect that area.=您无法保护该区域。
You can't protect that area: @1=您无法保护该区域:@1
You don't have the necessary privilege ("@1").=您没有所需权限(“@1”
You don't own that area.=您并不拥有该保护区。
You have @1 areas.=您有 @1 个保护区。
You have been granted control over area #@1. Type /list_areas to show your areas.=您已被授予保护区 #@1 的控制权。 使用 /list_areas 以查看您的保护区。
You have extended area protection limits ("areas_high_limit" privilege).=您拥有延伸保护权限拥有“areas_high_limit”权限
You have the necessary privilege ("@1").=您拥有所需权限(“@1”
You need to select an area first.=请先选择范围。
### chatcommands.lua ###
### pos.lua ###
<ID>=<保护区编号>
Invalid usage, see /help @1.=无效用法,请参见 /help @1。
### hud.lua ###
:open=:开放
Areas:=保护区:
### init.lua ###
Can administer areas.=可以管理保护区
Can protect areas.=可以进行保护
Can protect more, bigger areas.=进行保护时,数量以及体积上限提高。
### interact.lua ###
@1 is protected by @2.=@1 被 @2 保护。
### internal.lua ###
Area is too big.=区域过大。
Self protection is disabled or you do not have the necessary privilege.=自我保护被禁用,或您没有所需权限。
The area intersects with @1 [@2] (@3).=区域与 @1 [@2] (@3) 重叠。
You have reached the maximum amount of areas that you are allowed to protect.=您的现有保护区数量已达到所允许的上限。
### legacy.lua ###
<version>=<版本>
Converted areas saved. Done.=转换后的保护区已经保存。 完成。
Converting areas…=正在转换保护区……
Error loading legacy file: @1=载入旧档案时发生错误:@1
Invalid version number. (0 allowed)=无效版本号。允许值0
Legacy file loaded.=旧档案已载入。
Loads, converts, and saves the areas from a legacy save file.=载入、转换并储存旧存档中的保护区。
Table format updated.=表格格式已更新。
unnamed=未命名
### pos.lua ###
<not set>=<未设定>
Area @1 selected.=保护区 @1 已选择。
Area position @1 set to @2=保护区座标 @1 已设定为 @2。
Position @1 set to @2=保护区座标 @1 已设定为 @2。
Position @1: =保护区座标 @1
Select an area by ID.=通过保护区编号选择区域
Select position @1 by punching a node.=请击打方块以设定保护区座标 @1 的位置。
Select positions by punching two nodes.=请击打两个方块,以设定保护区座标的位置。
Set area protection region position @1 to your location or the one specified=将保护区座标 @1 的位置设定为您目前的位置或提供的座标
Set area protection region, position 1, or position 2 by punching nodes, or display the region=击打方块设定保护区座标,或获得其座标资讯
The area @1 does not exist.=保护区 @1 不存在。
Unable to get position.=无法获得座标。
Unknown subcommand: @1=子指令不明:@1
Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.=此服务器不支援相对座标。请更新Minetest至5.7.0或之后的版本。

137
locale/areas.zh_TW.tr Normal file
View File

@ -0,0 +1,137 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<保護區名稱>
<ID> [faction_name]=<保護區編號> [派系名稱]
<NewOwner>=<新擁有者>
<ParentID>=<上級保護區編號>
<PlayerName>=<玩家名稱>
<newName>=<新名稱>
@1 has given you control over the area "@2" (ID @3).=@1 已將保護區「@2」編號 @3的控制權交與您。
@1 spanning up to @2x@3x@4.=@1最大爲 @2x@3x@4。
A regular expression is required.=需要正則表達式。
Area @1 does not exist or is not owned by you.=保護區 @1 不存在,或並非由您擁有。
Area closed for faction members.=保護區已對派系成員關閉。
Area closed.=保護區已關閉。
Area does not exist.=保護區不存在。
Area is open for members of: @1=保護區已對以下派系的成員開放:@1
Area opened.=保護區已開放。
Area protected. ID: @1=成功進行保護。保護區編號:@1
Area renamed.=保護區已重新命名。
Area successfully moved.=成功移動保護區。
Change the owner of an area using its ID=通過保護區編號改變其擁有者
Faction doesn't exists=派系不存在
Find areas using a Lua regular expression=使用 Lua 正則表達式尋找保護區
Get information about area configuration and usage.=獲得保護區配置與使用的資訊。
Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=授予其他玩家已有保護區內一部分地方、兩個座標之間區域的的存取權。如果不想上級保護區被設定,請使用 /set_owner。
Invalid regular expression.=正則表達式無效。
Limit: @1 areas=限制:@1個保護區
Limit: no area count limit=限制:無數量限制
List your areas, or all areas if you are an admin.=列出您擁有的保護區,或對於管理員而言,列出所有保護區。
Move (or resize) an area to the current positions.=移動一保護區至目前座標,或改變其大小。
No matches found.=找不到匹配項。
No visible areas.=沒有保護區可供查閱。
Owner changed.=擁有者已修改。
Players with the "@1" privilege can protect up to @2 areas=擁有「@1」權限的玩家最多可以保護 @2 個保護區
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=授予其他玩家兩個座標之間區域的控制權,而不設定上級保護區
Protect your own area=對您自己的地區施加保護
Recursively remove areas using an ID=通過保護區編號移除保護區以及其所有子保護區
Remove an area using an ID=通過保護區編號移除一保護區
Removed area @1=保護區 @1 已移除
Removed area @1 and its sub areas.=保護區 @1 以及其所有子保護區已移除
Removes all ownerless areas=移除所有無主保護區
Rename an area that you own=重新命名您擁有的保護區
Self protection is disabled.=自我保護被禁用。
Self protection is enabled.=自我保護被啓用。
That area doesn't exist.=保護區不存在。
The player "@1" does not exist.=玩家「@1」不存在。
Toggle an area open (anyone can interact) or closed=切換保護區開放狀態(如開放,所有玩家均可互動)
Toggle an area open/closed for members in your faction.=切換保護區對派系成員的開放狀態
You are an area administrator ("areas" privilege).=您是保護區管理員擁有「area」權限
You can protect areas=您可以建立保護區
You can't protect that area.=您無法保護該區域。
You can't protect that area: @1=您無法保護該區域:@1
You don't have the necessary privilege ("@1").=您沒有所需權限(「@1」
You don't own that area.=您並不擁有改保護區。
You have @1 areas.=您有 @1 個保護區。
You have been granted control over area #@1. Type /list_areas to show your areas.=您已被授予保護區 #@1 的控制權。使用 /list_areas 以查看您的保護區。
You have extended area protection limits ("areas_high_limit" privilege).=您擁有延伸保護權限擁有「areas_high_limit」權限
You have the necessary privilege ("@1").=您擁有所需權限(「@1」
You need to select an area first.=請先選擇範圍。
### chatcommands.lua ###
### pos.lua ###
<ID>=<保護區編號>
Invalid usage, see /help @1.=無效用法,請參見 /help @1。
### hud.lua ###
:open=:開放
Areas:=保護區:
### init.lua ###
Can administer areas.=可以管理保護區
Can protect areas.=可以進行保護
Can protect more, bigger areas.=進行保護時,數量以及體積上限提高。
### interact.lua ###
@1 is protected by @2.=@1 被 @2 保護。
### internal.lua ###
Area is too big.=區域過大。
Self protection is disabled or you do not have the necessary privilege.=自我保護被禁用,或您沒有所需權限。
The area intersects with @1 [@2] (@3).=區域與 @1 [@2] (@3) 重疊。
You have reached the maximum amount of areas that you are allowed to protect.=您的現有保護區數量已達到所允許的上限。
### legacy.lua ###
<version>=<版本>
Converted areas saved. Done.=轉換後的保護區已經保存。完成。
Converting areas…=正在轉換保護區……
Error loading legacy file: @1=載入舊檔案時發生錯誤:@1
Invalid version number. (0 allowed)=無效版本號。允許值0
Legacy file loaded.=舊檔案已載入。
Loads, converts, and saves the areas from a legacy save file.=載入、轉換並儲存舊存檔中的保護區。
Table format updated.=表格格式已更新。
unnamed=未命名
### pos.lua ###
<not set>=<未設定>
Area @1 selected.=保護區 @1 已選擇。
Area position @1 set to @2=保護區座標 @1 已設定爲 @2。
Position @1 set to @2=保護區座標 @1 已設定爲 @2。
Position @1: =保護區座標 @1
Select an area by ID.=通過保護區編號選擇區域
Select position @1 by punching a node.=請擊打方塊,以設定保護區座標 @1 的位置。
Select positions by punching two nodes.=請擊打兩個方塊,以設定保護區座標的位置。
Set area protection region position @1 to your location or the one specified=將保護區座標 @1 的位置設定爲您目前的位置或提供的座標
Set area protection region, position 1, or position 2 by punching nodes, or display the region=擊打方塊設定保護區座標,或獲得其座標資訊
The area @1 does not exist.=保護區 @1 不存在。
Unable to get position.=無法獲得座標。
Unknown subcommand: @1=子指令不明:@1
Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.=此伺服器不支援相對座標。請更新Minetest至5.7.0或之後的版本。

137
locale/template.txt Normal file
View File

@ -0,0 +1,137 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=
<ID> [faction_name]=
<NewOwner>=
<ParentID>=
<PlayerName>=
<newName>=
@1 has given you control over the area "@2" (ID @3).=
@1 spanning up to @2x@3x@4.=
A regular expression is required.=
Area @1 does not exist or is not owned by you.=
Area closed for faction members.=
Area closed.=
Area does not exist.=
Area is open for members of: @1=
Area opened.=
Area protected. ID: @1=
Area renamed.=
Area successfully moved.=
Change the owner of an area using its ID=
Faction doesn't exists=
Find areas using a Lua regular expression=
Get information about area configuration and usage.=
Give a player access to a sub-area between two positions that have already been protected, Use set_owner if you don't want the parent to be set.=
Invalid regular expression.=
Limit: @1 areas=
Limit: no area count limit=
List your areas, or all areas if you are an admin.=
Move (or resize) an area to the current positions.=
No matches found.=
No visible areas.=
Owner changed.=
Players with the "@1" privilege can protect up to @2 areas=
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=
Protect your own area=
Recursively remove areas using an ID=
Remove an area using an ID=
Removed area @1=
Removed area @1 and its sub areas.=
Removes all ownerless areas=
Rename an area that you own=
Self protection is disabled.=
Self protection is enabled.=
That area doesn't exist.=
The player "@1" does not exist.=
Toggle an area open (anyone can interact) or closed=
Toggle an area open/closed for members in your faction.=
You are an area administrator ("areas" privilege).=
You can protect areas=
You can't protect that area.=
You can't protect that area: @1=
You don't have the necessary privilege ("@1").=
You don't own that area.=
You have @1 areas.=
You have been granted control over area #@1. Type /list_areas to show your areas.=
You have extended area protection limits ("areas_high_limit" privilege).=
You have the necessary privilege ("@1").=
You need to select an area first.=
### chatcommands.lua ###
### pos.lua ###
<ID>=
Invalid usage, see /help @1.=
### hud.lua ###
:open=
Areas:=
### init.lua ###
Can administer areas.=
Can protect areas.=
Can protect more, bigger areas.=
### interact.lua ###
@1 is protected by @2.=
### internal.lua ###
Area is too big.=
Self protection is disabled or you do not have the necessary privilege.=
The area intersects with @1 [@2] (@3).=
You have reached the maximum amount of areas that you are allowed to protect.=
### legacy.lua ###
<version>=
Converted areas saved. Done.=
Converting areas…=
Error loading legacy file: @1=
Invalid version number. (0 allowed)=
Legacy file loaded.=
Loads, converts, and saves the areas from a legacy save file.=
Table format updated.=
unnamed=
### pos.lua ###
<not set>=
Area @1 selected.=
Area position @1 set to @2=
Position @1 set to @2=
Position @1: =
Select an area by ID.=
Select position @1 by punching a node.=
Select positions by punching two nodes.=
Set area protection region position @1 to your location or the one specified=
Set area protection region, position 1, or position 2 by punching nodes, or display the region=
The area @1 does not exist.=
Unable to get position.=
Unknown subcommand: @1=
Relative coordinates is not supported on this server. Please upgrade Minetest to 5.7.0 or newer versions.=

2
mod.conf Normal file
View File

@ -0,0 +1,2 @@
name = areas
optional_depends = playerfactions

173
pos.lua
View File

@ -1,3 +1,4 @@
local S = minetest.get_translator("areas")
-- I could depend on WorldEdit for this, but you need to have the 'worldedit' -- I could depend on WorldEdit for this, but you need to have the 'worldedit'
-- permission to use those commands and you don't have -- permission to use those commands and you don't have
@ -11,109 +12,160 @@ areas.set_pos = {}
areas.pos1 = {} areas.pos1 = {}
areas.pos2 = {} areas.pos2 = {}
local LIMIT = 30992 -- this is due to MAPBLOCK_SIZE=16!
local function posLimit(pos)
return {
x = math.max(math.min(pos.x, LIMIT), -LIMIT),
y = math.max(math.min(pos.y, LIMIT), -LIMIT),
z = math.max(math.min(pos.z, LIMIT), -LIMIT)
}
end
local parse_relative_pos
if minetest.parse_relative_number then
parse_relative_pos = function(x_str, y_str, z_str, pos)
local x = pos and minetest.parse_relative_number(x_str, pos.x)
or tonumber(x_str)
local y = pos and minetest.parse_relative_number(y_str, pos.y)
or tonumber(y_str)
local z = pos and minetest.parse_relative_number(z_str, pos.z)
or tonumber(z_str)
if x and y and z then
return vector.new(x, y, z)
end
end
else
parse_relative_pos = function(x_str, y_str, z_str, pos)
local x = tonumber(x_str)
local y = tonumber(y_str)
local z = tonumber(z_str)
if x and y and z then
return vector.new(x, y, z)
elseif string.sub(x_str, 1, 1) == "~"
or string.sub(y_str, 1, 1) == "~"
or string.sub(z_str, 1, 1) == "~" then
return nil, S("Relative coordinates is not supported on this server. " ..
"Please upgrade Minetest to 5.7.0 or newer versions.")
end
end
end
minetest.register_chatcommand("select_area", { minetest.register_chatcommand("select_area", {
params = "<ID>", params = S("<ID>"),
description = "Select a area by id.", description = S("Select an area by ID."),
func = function(name, param) func = function(name, param)
local id = tonumber(param) local id = tonumber(param)
if not id then if not id then
return false, "Invalid usage, see /help select_area." return false, S("Invalid usage, see /help @1.", "select_area")
end end
if not areas.areas[id] then if not areas.areas[id] then
return false, "The area "..id.." does not exist." return false, S("The area @1 does not exist.", id)
end end
areas:setPos1(name, areas.areas[id].pos1) areas:setPos1(name, areas.areas[id].pos1)
areas:setPos2(name, areas.areas[id].pos2) areas:setPos2(name, areas.areas[id].pos2)
return true, "Area "..id.." selected." return true, S("Area @1 selected.", id)
end, end,
}) })
minetest.register_chatcommand("area_pos1", { minetest.register_chatcommand("area_pos1", {
params = "[X Y Z|X,Y,Z]", params = "[X Y Z|X,Y,Z]",
description = "Set area protection region position 1 to your" description = S("Set area protection region position @1 to your"
.." location or the one specified", .." location or the one specified", "1"),
privs = {}, privs = {},
func = function(name, param) func = function(name, param)
local pos = nil local pos
local found, _, x, y, z = param:find( local player = minetest.get_player_by_name(name)
"^(-?%d+)[, ](-?%d+)[, ](-?%d+)$") if player then
if found then pos = vector.round(player:get_pos())
pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
elseif param == "" then
local player = minetest.get_player_by_name(name)
if player then
pos = player:getpos()
else
return false, "Unable to get position."
end
else
return false, "Invalid usage, see /help area_pos1."
end end
pos = vector.round(pos) local found, _, x_str, y_str, z_str = param:find(
"^(~?-?%d*)[, ](~?-?%d*)[, ](~?-?%d*)$")
if found then
local get_pos, reason = parse_relative_pos(x_str, y_str, z_str, pos)
if get_pos then
pos = get_pos
elseif not get_pos and reason then
return false, reason
end
elseif param ~= "" then
return false, S("Invalid usage, see /help @1.", "area_pos1")
end
if not pos then
return false, S("Unable to get position.")
end
pos = posLimit(vector.round(pos))
areas:setPos1(name, pos) areas:setPos1(name, pos)
return true, "Area position 1 set to " return true, S("Area position @1 set to @2", "1",
..minetest.pos_to_string(pos) minetest.pos_to_string(pos))
end, end,
}) })
minetest.register_chatcommand("area_pos2", { minetest.register_chatcommand("area_pos2", {
params = "[X Y Z|X,Y,Z]", params = "[X Y Z|X,Y,Z]",
description = "Set area protection region position 2 to your" description = S("Set area protection region position @1 to your"
.." location or the one specified", .." location or the one specified", "2"),
func = function(name, param) func = function(name, param)
local pos = nil local pos
local found, _, x, y, z = param:find( local player = minetest.get_player_by_name(name)
"^(-?%d+)[, ](-?%d+)[, ](-?%d+)$") if player then
if found then pos = vector.round(player:get_pos())
pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
elseif param == "" then
local player = minetest.get_player_by_name(name)
if player then
pos = player:getpos()
else
return false, "Unable to get position."
end
else
return false, "Invalid usage, see /help area_pos2."
end end
pos = vector.round(pos) local found, _, x_str, y_str, z_str = param:find(
"^(~?-?%d*)[, ](~?-?%d*)[, ](~?-?%d*)$")
if found then
local get_pos, reason = parse_relative_pos(x_str, y_str, z_str, pos)
if get_pos then
pos = get_pos
elseif not get_pos and reason then
return false, reason
end
elseif param ~= "" then
return false, S("Invalid usage, see /help @1.", "area_pos2")
end
if not pos then
return false, S("Unable to get position.")
end
pos = posLimit(vector.round(pos))
areas:setPos2(name, pos) areas:setPos2(name, pos)
return true, "Area position 2 set to " return true, S("Area position @1 set to @2", "2",
..minetest.pos_to_string(pos) minetest.pos_to_string(pos))
end, end,
}) })
minetest.register_chatcommand("area_pos", { minetest.register_chatcommand("area_pos", {
params = "set/set1/set2/get", params = "set/set1/set2/get",
description = "Set area protection region, position 1, or position 2" description = S("Set area protection region, position 1, or position 2"
.." by punching nodes, or display the region", .." by punching nodes, or display the region"),
func = function(name, param) func = function(name, param)
if param == "set" then -- Set both area positions if param == "set" then -- Set both area positions
areas.set_pos[name] = "pos1" areas.set_pos[name] = "pos1"
return true, "Select positions by punching two nodes." return true, S("Select positions by punching two nodes.")
elseif param == "set1" then -- Set area position 1 elseif param == "set1" then -- Set area position 1
areas.set_pos[name] = "pos1only" areas.set_pos[name] = "pos1only"
return true, "Select position 1 by punching a node." return true, S("Select position @1 by punching a node.", "1")
elseif param == "set2" then -- Set area position 2 elseif param == "set2" then -- Set area position 2
areas.set_pos[name] = "pos2" areas.set_pos[name] = "pos2"
return true, "Select position 2 by punching a node." return true, S("Select position @1 by punching a node.", "2")
elseif param == "get" then -- Display current area positions elseif param == "get" then -- Display current area positions
local pos1str, pos2str = "Position 1: ", "Position 2: " local pos1str, pos2str = S("Position @1: ", "1"), S("Position @1: ", "2")
if areas.pos1[name] then if areas.pos1[name] then
pos1str = pos1str..minetest.pos_to_string(areas.pos1[name]) pos1str = pos1str..minetest.pos_to_string(areas.pos1[name])
else else
pos1str = pos1str.."<not set>" pos1str = pos1str..S("<not set>")
end end
if areas.pos2[name] then if areas.pos2[name] then
pos2str = pos2str..minetest.pos_to_string(areas.pos2[name]) pos2str = pos2str..minetest.pos_to_string(areas.pos2[name])
else else
pos2str = pos2str.."<not set>" pos2str = pos2str..S("<not set>")
end end
return true, pos1str.."\n"..pos2str return true, pos1str.."\n"..pos2str
else else
return false, "Unknown subcommand: "..param return false, S("Unknown subcommand: @1", param)
end end
end, end,
}) })
@ -130,12 +182,12 @@ function areas:getPos(playerName)
end end
function areas:setPos1(playerName, pos) function areas:setPos1(playerName, pos)
areas.pos1[playerName] = pos areas.pos1[playerName] = posLimit(pos)
areas.markPos1(playerName) areas.markPos1(playerName)
end end
function areas:setPos2(playerName, pos) function areas:setPos2(playerName, pos)
areas.pos2[playerName] = pos areas.pos2[playerName] = posLimit(pos)
areas.markPos2(playerName) areas.markPos2(playerName)
end end
@ -149,22 +201,22 @@ minetest.register_on_punchnode(function(pos, node, puncher)
areas.markPos1(name) areas.markPos1(name)
areas.set_pos[name] = "pos2" areas.set_pos[name] = "pos2"
minetest.chat_send_player(name, minetest.chat_send_player(name,
"Position 1 set to " S("Position @1 set to @2", "1",
..minetest.pos_to_string(pos)) minetest.pos_to_string(pos)))
elseif areas.set_pos[name] == "pos1only" then elseif areas.set_pos[name] == "pos1only" then
areas.pos1[name] = pos areas.pos1[name] = pos
areas.markPos1(name) areas.markPos1(name)
areas.set_pos[name] = nil areas.set_pos[name] = nil
minetest.chat_send_player(name, minetest.chat_send_player(name,
"Position 1 set to " S("Position @1 set to @2", "1",
..minetest.pos_to_string(pos)) minetest.pos_to_string(pos)))
elseif areas.set_pos[name] == "pos2" then elseif areas.set_pos[name] == "pos2" then
areas.pos2[name] = pos areas.pos2[name] = pos
areas.markPos2(name) areas.markPos2(name)
areas.set_pos[name] = nil areas.set_pos[name] = nil
minetest.chat_send_player(name, minetest.chat_send_player(name,
"Position 2 set to " S("Position @1 set to @2", "2",
..minetest.pos_to_string(pos)) minetest.pos_to_string(pos)))
end end
end end
end) end)
@ -252,4 +304,3 @@ minetest.register_entity("areas:pos2", {
areas.marker2[name] = nil areas.marker2[name] = nil
end, end,
}) })

View File

@ -2,42 +2,44 @@ local world_path = minetest.get_worldpath()
areas.config = {} areas.config = {}
local function setting(tp, name, default) local function setting(name, tp, default)
local full_name = "areas."..name local full_name = "areas." .. name
local value local value
if tp == "boolean" then if tp == "bool" then
value = minetest.setting_getbool(full_name) value = minetest.settings:get_bool(full_name)
default = value == nil and minetest.is_yes(default)
elseif tp == "string" then elseif tp == "string" then
value = minetest.setting_get(full_name) value = minetest.settings:get(full_name)
elseif tp == "position" then elseif tp == "v3f" then
value = minetest.setting_get_pos(full_name) value = minetest.setting_get_pos(full_name)
elseif tp == "number" then default = value == nil and minetest.string_to_pos(default)
value = tonumber(minetest.setting_get(full_name)) elseif tp == "float" or tp == "int" then
value = tonumber(minetest.settings:get(full_name))
local v, other = default:match("^(%S+) (.+)")
default = value == nil and tonumber(other and v or default)
else else
error("Invalid setting type!") error("Cannot parse setting type " .. tp)
end end
if value == nil then if value == nil then
value = default value = default
assert(default ~= nil, "Cannot parse default for " .. full_name)
end end
--print("add", name, default, value)
areas.config[name] = value areas.config[name] = value
end end
local file = io.open(areas.modpath .. "/settingtypes.txt", "r")
for line in file:lines() do
local name, tp, value = line:match("^areas%.(%S+) %(.*%) (%S+) (.*)")
if value then
setting(name, tp, value)
end
end
file:close()
-------------- --------------
-- Settings -- -- Settings --
-------------- --------------
setting("string", "filename", world_path.."/areas.dat") setting("filename", "string", world_path.."/areas.dat")
-- Allow players with a privilege create their own areas
-- within the maximum size and number.
setting("boolean", "self_protection", false)
setting("string", "self_protection_privilege", "interact")
setting("position", "self_protection_max_size", {x=64, y=128, z=64})
setting("number", "self_protection_max_areas", 4)
-- For players with the areas_high_limit privilege.
setting("position", "self_protection_max_size_high", {x=512, y=512, z=512})
setting("number", "self_protection_max_areas_high", 32)
-- legacy_table (owner_defs) compatibility. Untested and has known issues.
setting("boolean", "legacy_table", false)

37
settingtypes.txt Normal file
View File

@ -0,0 +1,37 @@
# This file is parsed in "settings.lua". Check regex first.
# Static paths do not work well with settings
#areas.filename (Configuration file path) string (world_path)/areas.dat
# Allow players with a privilege create their own areas using /protect
# within the specified size and amount limits.
areas.self_protection (Self protection) bool false
# Self protection: Privilege required to protect an area
areas.self_protection_privilege (Self protection: Required privs) string interact
# Refresh delay for the name displays in the HUD in seconds
areas.tick (HUD update delay) float 0.5 0 100
# Enable the legacy owner_defs metatable mode. Untested and possibly unstable
areas.legacy_table (Legacy owner_defs metatable) bool false
[Self protection (normal)]
# Self protection (normal): Maximal size of the protectable area
# Only enter positive whole numbers for the coordinate values or you'll mess up stuff.
areas.self_protection_max_size (Maximal area size) v3f (64, 128, 64)
# Self protection (normal): Maximal amount of protected areas per player
areas.self_protection_max_areas (Maximal area count) int 4
[Self protection (high)]
# Self protection (normal): Maximal size of the protectable area
# This setting applies for plyaers with the privilege 'areas_high_limit'
areas.self_protection_max_size_high (Maximal area size) v3f (512, 512, 512)
# Self protection (normal): Maximal amount of protected areas per player
# Only enter positive whole numbers for the coordinate values or you'll mess up stuff.
# This setting applies for plyaers with the privilege 'areas_high_limit'
areas.self_protection_max_areas_high (Maximal area count) float 32

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 B