removed xban2, notice, areas, areasprotector

(these are only useful on multiplayer servers)
This commit is contained in:
Vanessa Dannenberg 2020-04-05 22:19:24 -04:00
parent a8ed4410a6
commit e5357b2191
37 changed files with 0 additions and 3698 deletions

View File

@ -1,18 +0,0 @@
unused_args = false
allow_defined_top = true
read_globals = {
"DIR_DELIM",
"core",
"dump",
"vector", "nodeupdate",
"VoxelManip", "VoxelArea",
"PseudoRandom", "ItemStack",
"AreaStore",
"default",
table = { fields = { "copy", "getn" } }
}
globals = {
"minetest"
}

View File

@ -1,502 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -1,112 +0,0 @@
Areas mod for Minetest
======================
Dependencies
------------
Minetest 5.0.0+ is recommended, but 0.4.16+ should work as well.
Configuration
-------------
Open the tab `Settings -> All Settings -> Mods -> areas` to get a list of all
possible settings.
For server owners: Check `settingtypes.txt` and modify your `minetest.conf`
according to the wanted setting changes.
Tutorial
--------
1) Specify the corner positions of the area you would like to protect.
Use one of the following commands:
* `/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
set them one at a time.
* `/area_pos1/2` to set one of the positions to your current position.
* `/area_pos1/2 X Y Z` to set one of the positions to the specified
coordinates.
2) Protect the selected area by running one of the following commands:
* `/set_owner <OwnerName> <AreaName>` -- If you have the `areas` privilege.
* `/protect <AreaName>` -- If you have the `areas` privilege or the server
administrator has enabled area self-protection.
The area name is used only for informational purposes and has no functional
importance.
For example: `/set_owner SomePlayer Mese city`
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
select the corners of the sub-area as you did in step 1.
If your markers are still around your original area and you want to grant
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
markers and want to grant access to a full area.
The `/add_owner` command expects three arguments:
1. The ID number of the parent area (the area that you want to add a
sub-area to).
2. The name of the player that will own the sub-area.
3. The name of the sub-area. (can contain spaces)
For example: `/add_owner 123 BobTheBuilder Diamond lighthouse`
Commands
--------
* `/protect <AreaName>` -- Protects an area for yourself. (if
self-protection is enabled)
* `/set_owner <OwnerName> <AreaName>` -- Protects an area for a specified
player. (requires the `areas` privilege)
* `/add_owner <ParentID> <OwnerName> <ChildName>` -- Grants another player
control over part (or all) of an area.
* `/rename_area <ID> <NewName>` -- Renames an existing area.
* `/list_areas` -- Lists all of the areas that you own, or all areas if you
have the `areas` privilege.
* `/find_areas <Regex>` -- Finds areas using a Lua regular expresion.
For example, to find castles:
/find_areas [Cc]astle
* `/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.
If the removed area has no parent it's sub-areas will have no parent.
* `/recursive_remove_areas <ID>` -- Removes an area and all sub-areas of it.
* `/change_owner <ID> <NewOwner>` -- Change the owner of an area.
* `/area_info` -- Returns information about area configuration and usage.
* `/select_area <ID>` -- Sets the area positions to those of an existing
area.
* `/area_pos {set,set1,set2,get}` -- Sets the area positions by punching
nodes or shows the current area positions.
* `/area_pos1 [X,Y,Z|X Y Z]` -- Sets area position one to your position or
the one supplied.
* `/area_pos2 [X,Y,Z|X Y Z]` -- Sets area position two to your position or
the one supplied.
License
-------
Copyright (C) 2013 ShadowNinja
Licensed under the GNU LGPL version 2.1 or later.
See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt

View File

@ -1,165 +0,0 @@
local hudHandlers = {}
areas.registered_on_adds = {}
areas.registered_on_removes = {}
areas.registered_on_moves = {}
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.
function areas:getAreasAtPos(pos)
local res = {}
if self.store then
local a = self.store:get_areas_for_pos(pos, false, true)
for store_id, store_area in pairs(a) do
local id = tonumber(store_area.data)
res[id] = self.areas[id]
end
else
local px, py, pz = pos.x, pos.y, pos.z
for id, area in pairs(self.areas) do
local ap1, ap2 = area.pos1, area.pos2
if
(px >= ap1.x and px <= ap2.x) and
(py >= ap1.y and py <= ap2.y) and
(pz >= ap1.z and pz <= ap2.z) then
res[id] = area
end
end
end
return res
end
--- Returns areas that intersect with the passed area.
function areas:getAreasIntersectingArea(pos1, pos2)
local res = {}
if self.store then
local a = self.store:get_areas_in_area(pos1, pos2,
true, false, true)
for store_id, store_area in pairs(a) do
local id = tonumber(store_area.data)
res[id] = self.areas[id]
end
else
self:sortPos(pos1, pos2)
local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z
local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z
for id, area in pairs(self.areas) do
local ap1, ap2 = area.pos1, area.pos2
if
(ap1.x <= p2x and ap2.x >= p1x) and
(ap1.y <= p2y and ap2.y >= p1y) and
(ap1.z <= p2z and ap2.z >= p1z) then
-- Found an intersecting area.
res[id] = area
end
end
end
return res
end
-- Checks if the area is unprotected or owned by you
function areas:canInteract(pos, name)
if minetest.check_player_privs(name, self.adminPrivs) then
return true
end
local owned = false
for _, area in pairs(self:getAreasAtPos(pos)) do
if area.owner == name or area.open then
return true
elseif areas.factions_available and area.faction_open then
local faction_name = factions.get_player_faction(area.owner)
if faction_name ~= nil and faction_name == factions.get_player_faction(name) then
return true
end
end
owned = true
end
return not owned
end
-- Returns a table (list) of all players that own an area
function areas:getNodeOwners(pos)
local owners = {}
for _, area in pairs(self:getAreasAtPos(pos)) do
table.insert(owners, area.owner)
end
return owners
end
--- Checks if the area intersects with an area that the player can't interact in.
-- Note that this fails and returns false when the specified area is fully
-- owned by the player, but with multiple protection zones, none of which
-- cover the entire checked area.
-- @param name (optional) Player name. If not specified checks for any intersecting areas.
-- @param allow_open Whether open areas should be counted as if they didn't exist.
-- @return Boolean indicating whether the player can interact in that area.
-- @return Un-owned intersecting area ID, if found.
function areas:canInteractInArea(pos1, pos2, name, allow_open)
if name and minetest.check_player_privs(name, self.adminPrivs) then
return true
end
self:sortPos(pos1, pos2)
-- Intersecting non-owned area ID, if found.
local blocking_area = nil
local areas = self:getAreasIntersectingArea(pos1, pos2)
for id, area in pairs(areas) do
-- First check for a fully enclosing owned area.
-- A little optimization: isAreaOwner isn't necessary
-- here since we're iterating over all relevant areas.
if area.owner == name and
self:isSubarea(pos1, pos2, id) then
return true
end
-- Then check for intersecting non-owned (blocking) areas.
-- We don't bother with this check if we've already found a
-- blocking area, as the check is somewhat expensive.
-- The area blocks if the area is closed or open areas aren't
-- acceptable to the caller, and the area isn't owned.
-- Note: We can't return directly here, because there might be
-- an exclosing owned area that we haven't gotten to yet.
if not blocking_area and
(not allow_open or not area.open) and
(not name or not self:isAreaOwner(id, name)) then
blocking_area = id
end
end
if blocking_area then
return false, blocking_area
end
-- There are no intersecting areas or they are only partially
-- intersecting areas and they are all owned by the player.
return true
end

View File

@ -1,48 +0,0 @@
Areas mod API
===
API list
---
* `areas:registerHudHandler(handler)` - Registers a handler to add items to the Areas HUD. See [HUD](#hud).
* `areas:registerOnAdd(func(id, area))`
* `areas:registerOnRemove(func(id))`
* `areas:registerOnMove(func(id, area, pos1, pos2))`
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,421 +0,0 @@
local S = minetest.get_translator("areas")
minetest.register_chatcommand("protect", {
params = S("<AreaName>"),
description = S("Protect your own area"),
privs = {[areas.config.self_protection_privilege]=true},
func = function(name, param)
if param == "" then
return false, S("Invalid usage, see /help @1.", "protect")
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, S("You need to select an area first.")
end
minetest.log("action", "/protect invoked, owner="..name..
" AreaName="..param..
" StartPos="..minetest.pos_to_string(pos1)..
" EndPos=" ..minetest.pos_to_string(pos2))
local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
if not canAdd then
return false, S("You can't protect that area: @1", errMsg)
end
local id = areas:add(name, param, pos1, pos2, nil)
areas:save()
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("set_owner", {
params = S("<PlayerName>").." "..S("<AreaName>"),
description = S("Protect an area between two positions and give"
.." a player access to it without setting the parent of the"
.." area to any existing area"),
privs = areas.adminPrivs,
func = function(name, param)
local ownerName, areaName = param:match('^(%S+)%s(.+)$')
if not ownerName then
return false, S("Invalid usage, see /help @1.", "set_owner")
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, S("You need to select an area first.")
end
if not areas:player_exists(ownerName) then
return false, S("The player \"@1\" does not exist.", ownerName)
end
minetest.log("action", name.." runs /set_owner. Owner = "..ownerName..
" AreaName = "..areaName..
" StartPos = "..minetest.pos_to_string(pos1)..
" EndPos = " ..minetest.pos_to_string(pos2))
local id = areas:add(ownerName, areaName, pos1, pos2, nil)
areas:save()
minetest.chat_send_player(ownerName,
S("You have been granted control over area #@1. "..
"Type /list_areas to show your areas.", id))
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("add_owner", {
params = S("<ParentID>").." "..S("<PlayerName>").." "..S("<AreaName>"),
description = S("Give a player access to a sub-area beetween two"
.." positions that have already been protected,"
.." Use set_owner if you don't want the parent to be set."),
func = function(name, param)
local pid, ownerName, areaName
= param:match('^(%d+) ([^ ]+) (.+)$')
if not pid then
minetest.chat_send_player(name, S("Invalid usage, see /help @1.", "add_owner"))
return
end
local pos1, pos2 = areas:getPos(name)
if not (pos1 and pos2) then
return false, S("You need to select an area first.")
end
if not areas:player_exists(ownerName) then
return false, S("The player \"@1\" does not exist.", ownerName)
end
minetest.log("action", name.." runs /add_owner. Owner = "..ownerName..
" AreaName = "..areaName.." ParentID = "..pid..
" StartPos = "..pos1.x..","..pos1.y..","..pos1.z..
" EndPos = " ..pos2.x..","..pos2.y..","..pos2.z)
-- Check if this new area is inside an area owned by the player
pid = tonumber(pid)
if (not areas:isAreaOwner(pid, name)) or
(not areas:isSubarea(pos1, pos2, pid)) then
return false, S("You can't protect that area.")
end
local id = areas:add(ownerName, areaName, pos1, pos2, pid)
areas:save()
minetest.chat_send_player(ownerName,
S("You have been granted control over area #@1. "..
"Type /list_areas to show your areas.", id))
return true, S("Area protected. ID: @1", id)
end
})
minetest.register_chatcommand("rename_area", {
params = S("<ID>").." "..S("<newName>"),
description = S("Rename an area that you own"),
func = function(name, param)
local id, newName = param:match("^(%d+)%s(.+)$")
if not id then
return false, S("Invalid usage, see /help @1.", "rename_area")
end
id = tonumber(id)
if not id then
return false, S("That area doesn't exist.")
end
if not areas:isAreaOwner(id, name) then
return true, S("You don't own that area.")
end
areas.areas[id].name = newName
areas:save()
return true, S("Area renamed.")
end
})
minetest.register_chatcommand("find_areas", {
params = "<regexp>",
description = S("Find areas using a Lua regular expression"),
privs = areas.adminPrivs,
func = function(name, param)
if param == "" then
return false, S("A regular expression is required.")
end
-- Check expression for validity
local function testRegExp()
("Test [1]: Player (0,0,0) (0,0,0)"):find(param)
end
if not pcall(testRegExp) then
return false, S("Invalid regular expression.")
end
local matches = {}
for id, area in pairs(areas.areas) do
local str = areas:toString(id)
if str:find(param) then
table.insert(matches, str)
end
end
if #matches > 0 then
return true, table.concat(matches, "\n")
else
return true, S("No matches found.")
end
end
})
minetest.register_chatcommand("list_areas", {
description = S("List your areas, or all areas if you are an admin."),
func = function(name, param)
local admin = minetest.check_player_privs(name, areas.adminPrivs)
local areaStrings = {}
for id, area in pairs(areas.areas) do
if admin or areas:isAreaOwner(id, name) then
table.insert(areaStrings, areas:toString(id))
end
end
if #areaStrings == 0 then
return true, S("No visible areas.")
end
return true, table.concat(areaStrings, "\n")
end
})
minetest.register_chatcommand("recursive_remove_areas", {
params = S("<ID>"),
description = S("Recursively remove areas using an ID"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see"
.." /help @1.", "recursive_remove_areas")
end
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist or is"
.." not owned by you.", id)
end
areas:remove(id, true)
areas:save()
return true, S("Removed area @1 and it's sub areas.", id)
end
})
minetest.register_chatcommand("remove_area", {
params = S("<ID>"),
description = S("Remove an area using an ID"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help @1.", "remove_area")
end
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist or"
.." is not owned by you.", id)
end
areas:remove(id)
areas:save()
return true, S("Removed area @1", id)
end
})
minetest.register_chatcommand("change_owner", {
params = S("<ID>").." "..S("<NewOwner>"),
description = S("Change the owner of an area using its ID"),
func = function(name, param)
local id, newOwner = param:match("^(%d+)%s(%S+)$")
if not id then
return false, S("Invalid usage, see"
.." /help @1.", "change_owner")
end
if not areas:player_exists(newOwner) then
return false, S("The player \"@1\" does not exist.", newOwner)
end
id = tonumber(id)
if not areas:isAreaOwner(id, name) then
return false, S("Area @1 does not exist"
.." or is not owned by you.", id)
end
areas.areas[id].owner = newOwner
areas:save()
minetest.chat_send_player(newOwner,
S("@1 has given you control over the area \"@2\" (ID @3).",
name, areas.areas[id].name, id))
return true, S("Owner changed.")
end
})
minetest.register_chatcommand("area_open", {
params = S("<ID>"),
description = S("Toggle an area open (anyone can interact) or closed"),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help @1.", "area_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
local open = not areas.areas[id].open
-- Save false as nil to avoid inflating the DB.
areas.areas[id].open = open or nil
areas:save()
return true, open and S("Area opened.") or S("Area closed.")
end
})
if areas.factions_available then
minetest.register_chatcommand("area_faction_open", {
params = S("<ID>"),
description = S("Toggle an area open/closed for members in your faction."),
func = function(name, param)
local id = tonumber(param)
if not id 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
local open = not areas.areas[id].faction_open
-- Save false as nil to avoid inflating the DB.
areas.areas[id].faction_open = open or nil
areas:save()
return true, open and S("Area opened for faction members.")
or S("Area closed for faction members.")
end
})
end
minetest.register_chatcommand("move_area", {
params = S("<ID>"),
description = S("Move (or resize) an area to the current positions."),
privs = areas.adminPrivs,
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help @1.", "move_area")
end
local area = areas.areas[id]
if not area then
return false, S("Area does not exist.")
end
local pos1, pos2 = areas:getPos(name)
if not pos1 then
return false, S("You need to select an area first.")
end
areas:move(id, area, pos1, pos2)
areas:save()
return true, S("Area successfully moved.")
end,
})
minetest.register_chatcommand("area_info", {
description = S("Get information about area configuration and usage."),
func = function(name, param)
local lines = {}
local privs = minetest.get_player_privs(name)
-- Short (and fast to access) names
local cfg = areas.config
local self_prot = cfg.self_protection
local prot_priv = cfg.self_protection_privilege
local limit = cfg.self_protection_max_areas
local limit_high = cfg.self_protection_max_areas_high
local size_limit = cfg.self_protection_max_size
local size_limit_high = cfg.self_protection_max_size_high
local has_high_limit = privs.areas_high_limit
local has_prot_priv = not prot_priv or privs[prot_priv]
local can_prot = privs.areas or (self_prot and has_prot_priv)
local max_count = can_prot and
(has_high_limit and limit_high or limit) or 0
local max_size = has_high_limit and
size_limit_high or size_limit
-- Self protection information
local self_prot_line = self_prot and S("Self protection is enabled.") or
S("Self protection is disabled.")
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
table.insert(lines, S("You are an area"..
" administrator (\"areas\" privilege)."))
elseif has_high_limit then
table.insert(lines,
S("You have extended area protection"..
" limits (\"areas_high_limit\" privilege)."))
end
-- Area count
local area_num = 0
for id, area in pairs(areas.areas) do
if area.owner == name then
area_num = area_num + 1
end
end
table.insert(lines, S("You have @1 areas.", area_num))
-- Area limit
local area_limit_line = privs.areas and
S("Limit: no area count limit") or
S("Limit: @1 areas", max_count)
table.insert(lines, area_limit_line)
-- Area size limits
local function size_info(str, size)
table.insert(lines, S("@1 spanning up to @2x@3x@4.",
str, size.x, size.y, size.z))
end
local function priv_limit_info(lpriv, lmax_count, lmax_size)
size_info(S("Players with the \"@1\" privilege"..
" can protect up to @2 areas", lpriv, lmax_count),
lmax_size)
end
if self_prot then
if privs.areas then
priv_limit_info(prot_priv,
limit, size_limit)
priv_limit_info("areas_high_limit",
limit_high, size_limit_high)
elseif has_prot_priv then
size_info(S("You can protect areas"), max_size)
end
end
return true, table.concat(lines, "\n")
end,
})

View File

@ -1,72 +0,0 @@
-- This is inspired by the landrush mod by Bremaweb
local S = minetest.get_translator("areas")
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
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local pos = vector.round(player:get_pos())
pos = vector.apply(pos, function(p)
return math.max(math.min(p, 2147483), -2147483)
end)
local areaStrings = {}
for id, area in pairs(areas:getAreasAtPos(pos)) do
local faction_info = area.faction_open and areas.factions_available and
factions.get_player_faction(area.owner)
area.faction_open = faction_info
table.insert(areaStrings, ("%s [%u] (%s%s%s)")
:format(area.name, id, area.owner,
area.open and S(":open") or "",
faction_info and ":"..faction_info or ""))
end
for i, area in pairs(areas:getExternalHudEntries(pos)) do
local str = ""
if area.name then str = area.name .. " " end
if area.id then str = str.."["..area.id.."] " end
if area.owner then str = str.."("..area.owner..")" end
table.insert(areaStrings, str)
end
local areaString = S("Areas:")
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({
hud_elem_type = "text",
name = "Areas",
number = 0xFFFFFF,
position = {x=0, y=1},
offset = {x=8, y=-8},
text = areaString,
scale = {x=200, y=60},
alignment = {x=1, y=-1},
})
hud.oldAreas = areaString
return
elseif hud.oldAreas ~= areaString then
player:hud_change(hud.areasId, "text", areaString)
hud.oldAreas = areaString
end
end
end)
minetest.register_on_leaveplayer(function(player)
areas.hud[player:get_player_name()] = nil
end)

View File

@ -1,41 +0,0 @@
-- Areas mod by ShadowNinja
-- Based on node_ownership
-- License: LGPLv2+
areas = {}
areas.factions_available = minetest.global_exists("factions")
areas.adminPrivs = {areas=true}
areas.startTime = os.clock()
areas.modpath = minetest.get_modpath("areas")
dofile(areas.modpath.."/settings.lua")
dofile(areas.modpath.."/api.lua")
dofile(areas.modpath.."/internal.lua")
dofile(areas.modpath.."/chatcommands.lua")
dofile(areas.modpath.."/pos.lua")
dofile(areas.modpath.."/interact.lua")
dofile(areas.modpath.."/legacy.lua")
dofile(areas.modpath.."/hud.lua")
areas:load()
minetest.register_privilege("areas", {
description = "Can administer areas."
})
minetest.register_privilege("areas_high_limit", {
description = "Can can more, bigger areas."
})
if not minetest.registered_privileges[areas.config.self_protection_privilege] then
minetest.register_privilege(areas.config.self_protection_privilege, {
description = "Can protect areas.",
})
end
if minetest.settings:get_bool("log_mods") then
local diffTime = os.clock() - areas.startTime
minetest.log("action", "areas loaded in "..diffTime.."s.")
end

View File

@ -1,20 +0,0 @@
local S = minetest.get_translator("areas")
local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name)
if not areas:canInteract(pos, name) then
return true
end
return old_is_protected(pos, name)
end
minetest.register_on_protection_violation(function(pos, name)
if not areas:canInteract(pos, name) then
local owners = areas:getNodeOwners(pos)
minetest.chat_send_player(name,
S("@1 is protected by @2.",
minetest.pos_to_string(pos),
table.concat(owners, ", ")))
end
end)

View File

@ -1,306 +0,0 @@
local S = minetest.get_translator("areas")
function areas:player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil
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
function areas:save()
local datastr = minetest.serialize(self.areas)
if not datastr then
minetest.log("error", "[areas] Failed to serialize area data!")
return
end
return safe_file_write(self.config.filename, datastr)
end
-- Load the areas table from the save file
function areas:load()
local file, err = io.open(self.config.filename, "r")
if err then
self.areas = self.areas or {}
return err
end
self.areas = minetest.deserialize(file:read("*a"))
if type(self.areas) ~= "table" then
self.areas = {}
end
file:close()
self:populateStore()
end
--- Checks an AreaStore ID.
-- Deletes the AreaStore (falling back to the iterative method)
-- and prints an error message if the ID is invalid.
-- @return Whether the ID was valid.
function areas:checkAreaStoreId(sid)
if not sid then
minetest.log("error", "AreaStore failed to find an ID for an "
.."area! Falling back to iterative area checking.")
self.store = nil
self.store_ids = nil
end
return sid and true or false
end
-- Populates the AreaStore after loading, if needed.
function areas:populateStore()
if not rawget(_G, "AreaStore") then
return
end
local store = AreaStore()
local store_ids = {}
for id, area in pairs(areas.areas) do
local sid = store:insert_area(area.pos1,
area.pos2, tostring(id))
if not self:checkAreaStoreId(sid) then
return
end
store_ids[id] = sid
end
self.store = store
self.store_ids = store_ids
end
-- Finds the first usable index in a table
-- Eg: {[1]=false,[4]=true} -> 2
local function findFirstUnusedIndex(t)
local i = 0
repeat i = i + 1
until t[i] == nil
return i
end
--- Add a area.
-- @return The new area's ID.
function areas:add(owner, name, pos1, pos2, parent)
local id = findFirstUnusedIndex(self.areas)
self.areas[id] = {
name = name,
pos1 = pos1,
pos2 = pos2,
owner = owner,
parent = parent
}
for i=1, #areas.registered_on_adds do
areas.registered_on_adds[i](id, self.areas[id])
end
-- Add to AreaStore
if self.store then
local sid = self.store:insert_area(pos1, pos2, tostring(id))
if self:checkAreaStoreId(sid) then
self.store_ids[id] = sid
end
end
return id
end
--- Remove a area, and optionally it's children recursively.
-- If a area is deleted non-recursively the children will
-- have the removed area's parent as their new parent.
function areas:remove(id, recurse)
if recurse then
-- Recursively find child entries and remove them
local cids = self:getChildren(id)
for _, cid in pairs(cids) do
self:remove(cid, true)
end
else
-- Update parents
local parent = self.areas[id].parent
local children = self:getChildren(id)
for _, cid in pairs(children) do
-- The subarea parent will be niled out if the
-- removed area does not have a parent
self.areas[cid].parent = parent
end
end
for i=1, #areas.registered_on_removes do
areas.registered_on_removes[i](id)
end
-- Remove main entry
self.areas[id] = nil
-- Remove from AreaStore
if self.store then
self.store:remove_area(self.store_ids[id])
self.store_ids[id] = nil
end
end
--- Move an area.
function areas:move(id, area, pos1, pos2)
area.pos1 = pos1
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
self.store:remove_area(areas.store_ids[id])
local sid = self.store:insert_area(pos1, pos2, tostring(id))
if self:checkAreaStoreId(sid) then
self.store_ids[id] = sid
end
end
end
-- Checks if a area between two points is entirely contained by another area.
-- Positions must be sorted.
function areas:isSubarea(pos1, pos2, id)
local area = self.areas[id]
if not area then
return false
end
local ap1, ap2 = area.pos1, area.pos2
local ap1x, ap1y, ap1z = ap1.x, ap1.y, ap1.z
local ap2x, ap2y, ap2z = ap2.x, ap2.y, ap2.z
local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z
local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z
if
(p1x >= ap1x and p1x <= ap2x) and
(p2x >= ap1x and p2x <= ap2x) and
(p1y >= ap1y and p1y <= ap2y) and
(p2y >= ap1y and p2y <= ap2y) and
(p1z >= ap1z and p1z <= ap2z) and
(p2z >= ap1z and p2z <= ap2z) then
return true
end
end
-- Returns a table (list) of children of an area given it's identifier
function areas:getChildren(id)
local children = {}
for cid, area in pairs(self.areas) do
if area.parent and area.parent == id then
table.insert(children, cid)
end
end
return children
end
-- Checks if the user has sufficient privileges.
-- If the player is not a administrator it also checks
-- if the area intersects other areas that they do not own.
-- Also checks the size of the area and if the user already
-- has more than max_areas.
function areas:canPlayerAddArea(pos1, pos2, name)
local privs = minetest.get_player_privs(name)
if privs.areas then
return true
end
-- Check self protection privilege, if it is enabled,
-- and if the area is too big.
if not self.config.self_protection or
not privs[areas.config.self_protection_privilege] then
return false, S("Self protection is disabled or you do not have"
.." the necessary privilege.")
end
local max_size = privs.areas_high_limit and
self.config.self_protection_max_size_high or
self.config.self_protection_max_size
if
(pos2.x - pos1.x) > max_size.x or
(pos2.y - pos1.y) > max_size.y or
(pos2.z - pos1.z) > max_size.z then
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, S("You have reached the maximum amount of"
.." areas that you are allowed to protect.")
end
-- Check intersecting areas
local can, id = self:canInteractInArea(pos1, pos2, name)
if not can then
local area = self.areas[id]
return false, S("The area intersects with @1 [@2] (@3).",
area.name, id, area.owner)
end
return true
end
-- Given a id returns a string in the format:
-- "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children"
function areas:toString(id)
local area = self.areas[id]
local message = ("%s [%d]: %s %s %s"):format(
area.name, id, area.owner,
minetest.pos_to_string(area.pos1),
minetest.pos_to_string(area.pos2))
local children = areas:getChildren(id)
if #children > 0 then
message = message.." -> "..table.concat(children, ", ")
end
return message
end
-- Re-order areas in table by their identifiers
function areas:sort()
local sa = {}
for k, area in pairs(self.areas) do
if not area.parent then
table.insert(sa, area)
local newid = #sa
for _, subarea in pairs(self.areas) do
if subarea.parent == k then
subarea.parent = newid
table.insert(sa, subarea)
end
end
end
end
self.areas = sa
end
-- Checks if a player owns an area or a parent of it
function areas:isAreaOwner(id, name)
local cur = self.areas[id]
if cur and minetest.check_player_privs(name, self.adminPrivs) then
return true
end
while cur do
if cur.owner == name then
return true
elseif cur.parent then
cur = self.areas[cur.parent]
else
return false
end
end
return false
end

View File

@ -1,140 +0,0 @@
-- This file contains functions to convert from
-- the old areas format and other compatability code.
local S = minetest.get_translator("areas")
minetest.register_chatcommand("legacy_load_areas", {
params = S("<version>"),
description = S("Loads, converts, and saves the areas from"
.." a legacy save file."),
privs = {areas=true, server=true},
func = function(name, param)
minetest.chat_send_player(name, S("Converting areas…"))
local version = tonumber(param)
if version == 0 then
local err = areas:node_ownership_load()
if err then
minetest.chat_send_player(name, S("Error loading legacy file: @1", err))
return
end
else
minetest.chat_send_player(name, S("Invalid version number. (0 allowed)"))
return
end
minetest.chat_send_player(name, S("Legacy file loaded."))
for k, area in pairs(areas.areas) do
-- New position format
area.pos1 = {x=area.x1, y=area.y1, z=area.z1}
area.pos2 = {x=area.x2, y=area.y2, z=area.z2}
area.x1, area.y1, area.z1,
area.x2, area.y2, area.z2 =
nil, nil, nil, nil, nil, nil
-- Area positions sorting
areas:sortPos(area.pos1, area.pos2)
-- Add name
area.name = S("unnamed")
-- Remove ID
area.id = nil
end
minetest.chat_send_player(name, S("Table format updated."))
areas:save()
minetest.chat_send_player(name, S("Converted areas saved. Done."))
end
})
function areas:node_ownership_load()
local filename = minetest.get_worldpath().."/owners.tbl"
local tables, err
tables, err = loadfile(filename)
if err then
return err
end
tables = tables()
for idx = 1, #tables do
local tolinkv, tolinki = {}, {}
for i, v in pairs(tables[idx]) do
if type(v) == "table" and tables[v[1]] then
table.insert(tolinkv, {i, tables[v[1]]})
end
if type(i) == "table" and tables[i[1]] then
table.insert(tolinki, {i, tables[i[1]]})
end
end
-- link values, first due to possible changes of indices
for _, v in ipairs(tolinkv) do
tables[idx][v[1]] = v[2]
end
-- link indices
for _, v in ipairs(tolinki) do
tables[idx][v[2]], tables[idx][v[1]] = tables[idx][v[1]], nil
end
end
self.areas = tables[1]
end
-- Returns the name of the first player that owns an area
function areas.getNodeOwnerName(pos)
for id, area in pairs(areas:getAreasAtPos(pos)) do
return area.owner
end
return false
end
-- Checks if a node is owned by you
function areas.isNodeOwner(pos, name)
if minetest.check_player_privs(name, areas.adminPrivs) then
return true
end
for id, area in pairs(areas:getAreasAtPos(pos)) do
if name == area.owner then
return true
end
end
return false
end
-- Checks if an area has an owner
function areas.hasOwner(pos)
for id, area in pairs(areas:getAreasAtPos(pos)) do
return true
end
return false
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 S("unnamed")
a.id = nil
return rawset(areas.areas, key, a)
end
})
end

View File

@ -1,125 +0,0 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=<NomZone>
<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 opened for faction members.=Zone ouverte aux membres de la faction.
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.
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 beetween 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 it's sub areas.=Zone @1 et ses sous-zones supprimées.
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 :
### 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 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

View File

@ -1,125 +0,0 @@
# textdomain: areas
### chatcommands.lua ###
<AreaName>=
<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 opened for faction members.=
Area opened.=
Area protected. ID: @1=
Area renamed.=
Area successfully moved.=
Change the owner of an area using its ID=
Find areas using a Lua regular expression=
Get information about area configuration and usage.=
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=
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 it's sub 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:=
### 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=

View File

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

View File

@ -1,265 +0,0 @@
local S = minetest.get_translator("areas")
-- I could depend on WorldEdit for this, but you need to have the 'worldedit'
-- permission to use those commands and you don't have
-- /area_pos{1,2} [X Y Z|X,Y,Z].
-- Since this is mostly copied from WorldEdit it is mostly
-- licensed under the AGPL. (select_area is a exception)
areas.marker1 = {}
areas.marker2 = {}
areas.set_pos = {}
areas.pos1 = {}
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
minetest.register_chatcommand("select_area", {
params = S("<ID>"),
description = S("Select an area by ID."),
func = function(name, param)
local id = tonumber(param)
if not id then
return false, S("Invalid usage, see /help @1.", "select_area")
end
if not areas.areas[id] then
return false, S("The area @1 does not exist.", id)
end
areas:setPos1(name, areas.areas[id].pos1)
areas:setPos2(name, areas.areas[id].pos2)
return true, S("Area @1 selected.", id)
end,
})
minetest.register_chatcommand("area_pos1", {
params = "[X Y Z|X,Y,Z]",
description = S("Set area protection region position @1 to your"
.." location or the one specified", "1"),
privs = {},
func = function(name, param)
local pos
local found, _, x, y, z = param:find(
"^(-?%d+)[, ](-?%d+)[, ](-?%d+)$")
if found then
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:get_pos()
else
return false, S("Unable to get position.")
end
else
return false, S("Invalid usage, see /help @1.", "area_pos1")
end
pos = posLimit(vector.round(pos))
areas:setPos1(name, pos)
return true, S("Area position @1 set to @2", "1",
minetest.pos_to_string(pos))
end,
})
minetest.register_chatcommand("area_pos2", {
params = "[X Y Z|X,Y,Z]",
description = S("Set area protection region position @1 to your"
.." location or the one specified", "2"),
func = function(name, param)
local pos
local found, _, x, y, z = param:find(
"^(-?%d+)[, ](-?%d+)[, ](-?%d+)$")
if found then
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:get_pos()
else
return false, S("Unable to get position.")
end
else
return false, S("Invalid usage, see /help @1.", "area_pos2")
end
pos = posLimit(vector.round(pos))
areas:setPos2(name, pos)
return true, S("Area position @1 set to @2", "2",
minetest.pos_to_string(pos))
end,
})
minetest.register_chatcommand("area_pos", {
params = "set/set1/set2/get",
description = S("Set area protection region, position 1, or position 2"
.." by punching nodes, or display the region"),
func = function(name, param)
if param == "set" then -- Set both area positions
areas.set_pos[name] = "pos1"
return true, S("Select positions by punching two nodes.")
elseif param == "set1" then -- Set area position 1
areas.set_pos[name] = "pos1only"
return true, S("Select position @1 by punching a node.", "1")
elseif param == "set2" then -- Set area position 2
areas.set_pos[name] = "pos2"
return true, S("Select position @1 by punching a node.", "2")
elseif param == "get" then -- Display current area positions
local pos1str, pos2str = S("Position @1: ", "1"), S("Position @1: ", "2")
if areas.pos1[name] then
pos1str = pos1str..minetest.pos_to_string(areas.pos1[name])
else
pos1str = pos1str..S("<not set>")
end
if areas.pos2[name] then
pos2str = pos2str..minetest.pos_to_string(areas.pos2[name])
else
pos2str = pos2str..S("<not set>")
end
return true, pos1str.."\n"..pos2str
else
return false, S("Unknown subcommand: @1", param)
end
end,
})
function areas:getPos(playerName)
local pos1, pos2 = areas.pos1[playerName], areas.pos2[playerName]
if not (pos1 and pos2) then
return nil
end
-- Copy positions so that the area table doesn't contain multiple
-- references to the same position.
pos1, pos2 = vector.new(pos1), vector.new(pos2)
return areas:sortPos(pos1, pos2)
end
function areas:setPos1(playerName, pos)
areas.pos1[playerName] = posLimit(pos)
areas.markPos1(playerName)
end
function areas:setPos2(playerName, pos)
areas.pos2[playerName] = posLimit(pos)
areas.markPos2(playerName)
end
minetest.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name()
-- Currently setting position
if name ~= "" and areas.set_pos[name] then
if areas.set_pos[name] == "pos1" then
areas.pos1[name] = pos
areas.markPos1(name)
areas.set_pos[name] = "pos2"
minetest.chat_send_player(name,
S("Position @1 set to @2", "1",
minetest.pos_to_string(pos)))
elseif areas.set_pos[name] == "pos1only" then
areas.pos1[name] = pos
areas.markPos1(name)
areas.set_pos[name] = nil
minetest.chat_send_player(name,
S("Position @1 set to @2", "1",
minetest.pos_to_string(pos)))
elseif areas.set_pos[name] == "pos2" then
areas.pos2[name] = pos
areas.markPos2(name)
areas.set_pos[name] = nil
minetest.chat_send_player(name,
S("Position @1 set to @2", "2",
minetest.pos_to_string(pos)))
end
end
end)
-- Modifies positions `pos1` and `pos2` so that each component of `pos1`
-- is less than or equal to its corresponding component of `pos2`,
-- returning the two positions.
function areas:sortPos(pos1, pos2)
if pos1.x > pos2.x then
pos2.x, pos1.x = pos1.x, pos2.x
end
if pos1.y > pos2.y then
pos2.y, pos1.y = pos1.y, pos2.y
end
if pos1.z > pos2.z then
pos2.z, pos1.z = pos1.z, pos2.z
end
return pos1, pos2
end
-- Marks area position 1
areas.markPos1 = function(name)
local pos = areas.pos1[name]
if areas.marker1[name] ~= nil then -- Marker already exists
areas.marker1[name]:remove() -- Remove marker
areas.marker1[name] = nil
end
if pos ~= nil then -- Add marker
areas.marker1[name] = minetest.add_entity(pos, "areas:pos1")
areas.marker1[name]:get_luaentity().active = true
end
end
-- Marks area position 2
areas.markPos2 = function(name)
local pos = areas.pos2[name]
if areas.marker2[name] ~= nil then -- Marker already exists
areas.marker2[name]:remove() -- Remove marker
areas.marker2[name] = nil
end
if pos ~= nil then -- Add marker
areas.marker2[name] = minetest.add_entity(pos, "areas:pos2")
areas.marker2[name]:get_luaentity().active = true
end
end
minetest.register_entity("areas:pos1", {
initial_properties = {
visual = "cube",
visual_size = {x=1.1, y=1.1},
textures = {"areas_pos1.png", "areas_pos1.png",
"areas_pos1.png", "areas_pos1.png",
"areas_pos1.png", "areas_pos1.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
},
on_step = function(self, dtime)
if self.active == nil then
self.object:remove()
end
end,
on_punch = function(self, hitter)
self.object:remove()
local name = hitter:get_player_name()
areas.marker1[name] = nil
end,
})
minetest.register_entity("areas:pos2", {
initial_properties = {
visual = "cube",
visual_size = {x=1.1, y=1.1},
textures = {"areas_pos2.png", "areas_pos2.png",
"areas_pos2.png", "areas_pos2.png",
"areas_pos2.png", "areas_pos2.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
},
on_step = function(self, dtime)
if self.active == nil then
self.object:remove()
end
end,
on_punch = function(self, hitter)
self.object:remove()
local name = hitter:get_player_name()
areas.marker2[name] = nil
end,
})

View File

@ -1,46 +0,0 @@
local world_path = minetest.get_worldpath()
areas.config = {}
local function setting(name, tp, default)
local full_name = "areas." .. name
local value
if tp == "bool" then
value = minetest.settings:get_bool(full_name)
default = value == nil and minetest.is_yes(default)
elseif tp == "string" then
value = minetest.settings:get(full_name)
elseif tp == "v3f" then
value = minetest.setting_get_pos(full_name)
default = value == nil and minetest.string_to_pos(default)
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
error("Cannot parse setting type " .. tp)
end
if value == nil then
value = default
assert(default ~= nil, "Cannot parse default for " .. full_name)
end
--print("add", name, default, value)
areas.config[name] = value
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 --
--------------
setting("filename", "string", world_path.."/areas.dat")

View File

@ -1,38 +0,0 @@
# 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: 142 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

View File

@ -1,2 +0,0 @@
areas
basic_materials

View File

@ -1,261 +0,0 @@
local creative_mode = minetest.setting_getbool("creative_mode")
local function cyan(str)
return minetest.colorize("#00FFFF",str)
end
local function red(str)
return minetest.colorize("#FF5555",str)
end
local radius_large = minetest.setting_get("areasprotector_radius_large")
or minetest.setting_get("areasprotector_radius")
or 16
local height_large = minetest.setting_get("areasprotector_height_large")
or minetest.setting_get("areasprotector_radius_large")
or minetest.setting_get("areasprotector_radius")
or 16
local radius_small = minetest.setting_get("areasprotector_radius_small")
or 7
local height_small = minetest.setting_get("areasprotector_height_small")
or minetest.setting_get("areasprotector_radius_small")
or 7
local function remove_display(pos)
local objs = minetest.get_objects_inside_radius(pos, 0.5)
for _,o in pairs(objs) do
o:remove()
end
end
local function on_place(itemstack, player, pointed, radius, height, sizeword)
local pos = pointed.above
local pos1 = vector.add(pos,vector.new(radius, height, radius))
local pos2 = vector.add(pos,vector.new(-radius, -height, -radius))
local name = player:get_player_name()
local perm,err = areas:canPlayerAddArea(pos1,pos2,name)
if not perm then
minetest.chat_send_player(name,red("You are not allowed to protect that area: ")..err)
return itemstack
end
local id = areas:add(name,"Protected by Protector Block at "..minetest.pos_to_string(pos, 0),pos1,pos2)
areas:save()
local msg = string.format("The area from %s to %s has been protected as #%s",cyan(minetest.pos_to_string(pos1)),cyan(minetest.pos_to_string(pos2)),cyan(id))
minetest.chat_send_player(name,msg)
minetest.set_node(pos,{name="areasprotector:protector_"..sizeword})
local meta = minetest.get_meta(pos)
local infotext = string.format("Protecting area %d owned by %s",id,name)
meta:set_string("infotext",infotext)
meta:set_int("area_id",id)
meta:set_string("owner",name)
if not minetest.setting_getbool("creative_mode") then
itemstack:take_item()
end
return itemstack
end
local function after_dig(pos, oldnode, oldmetadata, digger, sizeword)
if oldmetadata and oldmetadata.fields then
local owner = oldmetadata.fields.owner
local id = tonumber(oldmetadata.fields.area_id)
local playername = digger:get_player_name()
if areas.areas[id] and areas:isAreaOwner(id,owner) then
if digger:get_player_control().sneak then
local inv = digger:get_inventory()
if not creative_mode then
if inv:room_for_item("main", "default:steel_ingot 6") then
inv:remove_item("main", "areasprotector:protector_"..sizeword.." 1")
inv:add_item("main", "default:steel_ingot 6")
else
minetest.chat_send_player(playername, "No room for the replacement ingots, just digging the protector and deleting the area normally.")
areas:remove(id)
areas:save()
end
else
inv:remove_item("main", "areasprotector:protector_"..sizeword.." 1")
end
else
areas:remove(id)
areas:save()
end
end
end
end
local function on_punch(pos, node, puncher, sizeword)
local objs = minetest.get_objects_inside_radius(pos,.5) -- a radius of .5 since the entity serialization seems to be not that precise
local removed = false
for _, o in pairs(objs) do
if (not o:is_player()) and o:get_luaentity().name == "areasprotector:display_"..sizeword then
o:remove()
removed = true
end
end
if not removed then -- nothing was removed: there wasn't the entity
minetest.add_entity(pos, "areasprotector:display_"..sizeword)
minetest.after(4, remove_display, pos)
end
end
local function on_step(self, dtime, sizeword)
if minetest.get_node(self.object:getpos()).name ~= "areasprotector:protector_"..sizeword then
self.object:remove()
return
end
end
local function make_display_nodebox(radius, height)
local nb_radius = radius + 0.55
local nb_height = height + 0.55
local t = {
-- sides
{ -nb_radius, -nb_height, -nb_radius, -nb_radius, nb_height, nb_radius },
{ -nb_radius, -nb_height, nb_radius, nb_radius, nb_height, nb_radius },
{ nb_radius, -nb_height, -nb_radius, nb_radius, nb_height, nb_radius },
{ -nb_radius, -nb_height, -nb_radius, nb_radius, nb_height, -nb_radius },
-- top
{ -nb_radius, nb_height, -nb_radius, nb_radius, nb_height, nb_radius },
-- bottom
{ -nb_radius, -nb_height, -nb_radius, nb_radius, -nb_height, nb_radius },
-- middle (surround protector)
{-.55,-.55,-.55, .55,.55,.55},
}
return t
end
local nbox = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 },
}
minetest.register_node("areasprotector:protector_large", {
description = "Protector Block (large volume)",
groups = {cracky=1},
tiles = {
"default_steel_block.png",
"default_steel_block.png",
"default_steel_block.png^areasprotector_large_overlay.png^basic_materials_padlock.png"
},
paramtype = "light",
drawtype = "nodebox",
node_box = nbox,
on_place = function(itemstack, player, pointed_thing)
on_place(itemstack, player, pointed_thing, radius_large, height_large, "large")
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
after_dig(pos, oldnode, oldmetadata, digger, "large")
end,
on_punch = function(pos, node, puncher)
on_punch(pos, node, puncher, "large")
end
})
minetest.register_node("areasprotector:protector_small", {
description = "Protector Block (small volume)",
groups = {cracky=1},
tiles = {
"default_steel_block.png",
"default_steel_block.png",
"default_steel_block.png^basic_materials_padlock.png"
},
paramtype = "light",
drawtype = "nodebox",
node_box = nbox,
on_place = function(itemstack, player, pointed_thing)
on_place(itemstack, player, pointed_thing, radius_small, height_small, "small")
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
after_dig(pos, oldnode, oldmetadata, digger, "small")
end,
on_punch = function(pos, node, puncher)
on_punch(pos, node, puncher, "small")
end
})
-- entities code below (and above) mostly copied-pasted from Zeg9's protector mod
-- wielditem seems to be scaled to 1.5 times original node size
local vsize = {x=1.0/1.5, y=1.0/1.5}
local ecbox = {0, 0, 0, 0, 0, 0}
minetest.register_entity("areasprotector:display_large", {
physical = false,
collisionbox = ecbox,
visual = "wielditem",
visual_size = vsize,
textures = {"areasprotector:display_node_large"},
on_step = function(self, dtime)
on_step(self, dtime, "large")
end
})
minetest.register_entity("areasprotector:display_small", {
physical = false,
collisionbox = ecbox,
visual = "wielditem",
visual_size = vsize,
textures = {"areasprotector:display_node_small"},
on_step = function(self, dtime)
on_step(self, dtime, "small")
end
})
minetest.register_node("areasprotector:display_node_large", {
tiles = {"areasprotector_display.png"},
walkable = false,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = make_display_nodebox(radius_large, height_large)
},
selection_box = {
type = "regular",
},
paramtype = "light",
groups = {dig_immediate=3,not_in_creative_inventory=1},
drop = ""
})
minetest.register_node("areasprotector:display_node_small", {
tiles = {"areasprotector_display.png"},
walkable = false,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = make_display_nodebox(radius_small, height_small)
},
selection_box = {
type = "regular",
},
paramtype = "light",
groups = {dig_immediate=3,not_in_creative_inventory=1},
drop = ""
})
minetest.register_craft({
output = "areasprotector:protector_small 2",
type = "shapeless",
recipe = {"default:steelblock","basic_materials:padlock"},
})
minetest.register_craft({
output = "areasprotector:protector_large",
type = "shapeless",
recipe = {
"areasprotector:protector_small",
"areasprotector:protector_small",
"areasprotector:protector_small",
"areasprotector:protector_small",
"areasprotector:protector_small",
"areasprotector:protector_small",
"areasprotector:protector_small",
"areasprotector:protector_small"
}
})
minetest.register_alias("areasprotector:protector", "areasprotector:protector_large")
minetest.register_alias("areasprotector:display_node", "areasprotector:display_node_large")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

View File

@ -1,53 +0,0 @@
notice = { }
function notice.send(target, text)
local player = minetest.get_player_by_name(target)
if not player then
return false, ("There's no player named '%s'."):format(target)
end
local fs = { }
--[[
for _, line in ipairs(text:split("|")) do
table.insert(fs, ("label[1,%f;%s]"):format(y+1, minetest.formspec_escape(line)))
y = y + 0.5
end
--]]
local lines = { }
for i, line in ipairs(text:split("|")) do
local lt = { }
for i = 1, #line, 40 do
table.insert(lt, line:sub(i, i+39))
end
lines[i] = table.concat(lt, "\n")
end
text = table.concat(lines, "\n")
text = minetest.formspec_escape(text)
table.insert(fs, "size[8,4]")
table.insert(fs, "label[1,.2;"..text.."]")
table.insert(fs, "button_exit[3,3.2;2,0.5;ok;OK]")
fs = table.concat(fs)
minetest.after(0.5, function()
minetest.show_formspec(target, "notice:notice", fs)
end)
return true
end
minetest.register_privilege("notice", "Send notices to players.")
minetest.register_chatcommand("notice", {
params = "<player> <text>",
privs = { notice=true, },
description = "Show a notice to a player.",
func = function(name, params)
local target, text = params:match("(%S+)%s+(.+)")
if not (target and text) then
return false, "Usage: /notice <player> <text>"
end
local ok, err = notice.send(target, text)
if not ok then
return false, err
end
return true, "Notice sent!"
end,
})

View File

@ -1,7 +0,0 @@
unused_args = false
allow_defined_top = true
read_globals = {
"minetest",
}

View File

@ -1,112 +0,0 @@
# Extended Ban Mod for Minetest
This mod attempts to be an improvement to Minetest's ban system.
* It supports normal bans and temporary bans (from 60 seconds up to the end of
time, with 1 second granularity).
* Records and joins all accounts using the same IP address and several IP
addresses using the same name into a single record, and can ban/unban them as
a single user.
* Can ban offline players if you know their IP or username.
* Holds a record of bans for each user, so moderators and administrators can
consult it to know if a player is a repeat offender.
* Does not modify the default ban database in any way (`ipban.txt').
* Has an API to ban and check the ban database to allows other mods to manage
users (for example, anticheat mods).
## Chat commands
The mod provides the following chat commands. All commands require the `ban`
privilege.
### `xban`
Bans a player permanently.
**Usage:** `/xban <player_or_ip> <reason>`
**Example:** `/xban 127.0.0.1 Some reason.`
### `xtempban`
Bans a player temporarily.
**Usage:** `/xtempban <player_or_ip> <time> <reason>`
The `time` parameter is a string in the format `<count><unit>` where `<unit>`
is one of `s` for seconds, `m` for minutes, `h` for hours, `D` for days, `W`
for weeks, `M` for months, or `Y` for years. If the unit is omitted, it is
assumed to mean seconds. For example, `42s` means 42 seconds, `1337m` 1337
minutes, and so on. You can chain more than one such group and they will add
up. For example, `1Y3M3D7h` will ban for 1 year, 3 months, 3 days and 7 hours.
**Example:** `/xtempban Joe 3600 Some reason.`
### `xunban`
Unbans a player.
**Usage:** `/xunban <player_or_ip>`
**Example:** `/xunban Joe`
### `xban_record`
Shows the ban record on chat.
**Usage:** `/xban_record <player_or_ip>`
This prints one ban entry per line, with the time the ban came into effect,
the expiration time (if applicable), the reason, and the source of the ban.
The record is printed to chat with one entry per line.
**Example:** `/xban_record Joe`
### `xban_wl`
Manages the whitelist.
**Usage:** `/xban_wl (add|del|get) <player_or_ip>`
Whitelisted players are allowed on the server even if it's otherwise marked
as banned. This is useful to only allow certain users from shared computers,
for example.
The `add` subcommand adds the player to the whitelist. The `del` subcommand
removes the player from the whitelist. The `get` subcommand checks if the
player is in the whitelist, and prints the status to chat.
**Example:** `/xban_record add Jane`
### `xban_gui`
Shows a form to consult the database interactively.
**Usage:** `/xban_gui`
## Administrator commands
The following commands require the `server` privilege, so they are only
available to server administrators.
### `xban_dbi`
Imports ban entries from other database formats.
**Usage:** `/xban_dbi <importer>`
The `importer` argument specifies from which database to import. These are
the supported import plugins at the time of writing:
* `minetest`: Import entries from Minetest's ban list (`ipban.txt`).
* `v1`: Old format used by xban (`players.iplist`).
* `v2`: Old format used by xban (`players.iplist.v2`).
**Example:** `/xban_dbi minetest`
### `xban_cleanup`
Removes all non-banned entries from the xban db.
**Usage:** `/xban_cleanup`

View File

@ -1,15 +0,0 @@
{
"name": "xban2",
"description": "Ban system extension with support for temporary bans.",
"keywords": [
"ban",
"administration",
"system",
"server"
],
"homepage": "http://github.com/kaeza/minetest-xban2.git",
"authors": [
"Diego Martínez <lkaezadl3@yahoo.com>"
],
"license": "BSD 2-Clause"
}

View File

@ -1,38 +0,0 @@
xban.importers = { }
dofile(xban.MP.."/importers/minetest.lua")
dofile(xban.MP.."/importers/v1.lua")
dofile(xban.MP.."/importers/v2.lua")
minetest.register_chatcommand("xban_dbi", {
description = "Import old databases",
params = "<importer>",
privs = { server=true },
func = function(name, params)
if params == "--list" then
local importers = { }
for importer in pairs(xban.importers) do
table.insert(importers, importer)
end
minetest.chat_send_player(name,
("[xban] Known importers: %s"):format(
table.concat(importers, ", ")))
return
elseif not xban.importers[params] then
minetest.chat_send_player(name,
("[xban] Unknown importer `%s'"):format(params))
minetest.chat_send_player(name, "[xban] Try `--list'")
return
end
local f = xban.importers[params]
local ok, err = f()
if ok then
minetest.chat_send_player(name,
"[xban] Import successfull")
else
minetest.chat_send_player(name,
("[xban] Import failed: %s"):format(err))
end
end,
})

View File

@ -1,32 +0,0 @@
## Extended Ban Mod API
### ban_player
`xban.ban_player(player_or_ip, source, expires, reason)`
Ban a player and all of his/her alternative names and IPs.
#### Arguments:
* `player_or_ip` - Player to search for and ban. See note 1 below.
* `source` - Source of the ban. See note 2 below.
* `expires` - Time at which the ban expires. If nil, ban is permanent.
* `reason` - Reason for ban.
### unban_player
`xban.unban_player(player_or_ip, source)`
Unban a player and all of his/her alternative names and IPs.
#### Arguments:
* `player_or_ip` - Player to search for and unban.
* `source` - Source of the ban. See note 2 below.
### Notes
* 1: If player is currently online, all his accounts are kicked.
* 2: Mods using the xban API are advised to use the `"modname:source"`
format for `source` (for example: `"anticheat:main"`).

View File

@ -1,45 +0,0 @@
Database is a regular Lua script that returns a table.
Table has a single named field `timestamp' containing the time_t the
DB was last saved. It's not used in the mod and is only meant for
external use (I don't find filesystem timestamps too reliable).
Next is a simple array (number indices) of entries.
Each entry contains following fields:
[1] = {
-- Names/IPs associated with this entry
names = {
["foo"] = true,
["bar"] = true,
["123.45.67.89"] = true,
},
banned = true, -- Whether this user is banned
-- Other fields do not apply if false
time = 12341234, -- Time of last ban (*1)
expires = 43214321 -- Time at which ban expires (*2)
-- If nil, permanent ban
reason = "asdf", -- Reason for ban
source = "qwerty", -- Source of ban (*2)
record = {
[1] = {
source = "asdf",
reason = "qwerty",
time = 12341234,
expires = 43214321,
},
[1] = {
source = "asdf",
reason = "Unbanned", -- When unbanned
time = 12341234,
},
},
}
Notes:
(*1) All times are expressed in whatever unit `os.time()' uses
(`time_t' on most (all?) systems).
(*2) Mods using the xban API are advised to use the "modname:source"
format for `source' (for example: "anticheat:main").

View File

@ -1,140 +0,0 @@
local FORMNAME = "xban2:main"
local MAXLISTSIZE = 100
local strfind, format = string.find, string.format
local ESC = minetest.formspec_escape
local function make_list(filter)
filter = filter or ""
local list, n, dropped = { }, 0, false
for k in minetest.get_auth_handler().iterate() do
if strfind(k, filter, 1, true) then
if n >= MAXLISTSIZE then
dropped = true
break
end
n=n+1 list[n] = k
end
end
table.sort(list)
return list, dropped
end
local states = { }
local function get_state(name)
local state = states[name]
if not state then
state = { index=1, filter="" }
states[name] = state
state.list, state.dropped = make_list()
end
return state
end
local function get_record_simple(name)
local e = xban.find_entry(name)
if not e then
return nil, ("No entry for `%s'"):format(name)
elseif (not e.record) or (#e.record == 0) then
return nil, ("`%s' has no ban records"):format(name)
end
local record = { }
for _, rec in ipairs(e.record) do
local msg = (os.date("%Y-%m-%d %H:%M:%S", rec.time).." | "
..(rec.reason or "No reason given."))
table.insert(record, msg)
end
return record, e.record
end
local function make_fs(name)
local state = get_state(name)
local list, filter = state.list, state.filter
local pli, ei = state.player_index or 1, state.entry_index or 0
if pli > #list then
pli = #list
end
local fs = {
"size[16,12]",
"label[0,-.1;Filter]",
"field[1.5,0;12.8,1;filter;;"..ESC(filter).."]",
"button[14,-.3;2,1;search;Search]",
}
local fsn = #fs
fsn=fsn+1 fs[fsn] = format("textlist[0,.8;4,9.3;player;%s;%d;0]",
table.concat(list, ","), pli)
local record_name = list[pli]
if record_name then
local record, e = get_record_simple(record_name)
if record then
for i, r in ipairs(record) do
record[i] = ESC(r)
end
fsn=fsn+1 fs[fsn] = format(
"textlist[4.2,.8;11.7,9.3;entry;%s;%d;0]",
table.concat(record, ","), ei)
local rec = e[ei]
if rec then
fsn=fsn+1 fs[fsn] = format("label[0,10.3;%s]",
ESC("Source: "..(rec.source or "<none>")
.."\nTime: "..os.date("%c", rec.time)
.."\n"..(rec.expires and
os.date("%c", rec.expires) or "")),
pli)
end
else
fsn=fsn+1 fs[fsn] = "textlist[4.2,.8;11.7,9.3;err;"..ESC(e)..";0]"
fsn=fsn+1 fs[fsn] = "label[0,10.3;"..ESC(e).."]"
end
else
local e = "No entry matches the query."
fsn=fsn+1 fs[fsn] = "textlist[4.2,.8;11.7,9.3;err;"..ESC(e)..";0]"
fsn=fsn+1 fs[fsn] = "label[0,10.3;"..ESC(e).."]"
end
return table.concat(fs)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then return end
local name = player:get_player_name()
if not minetest.check_player_privs(name, { ban=true }) then
minetest.log("warning",
"[xban2] Received fields from unauthorized user: "..name)
return
end
local state = get_state(name)
if fields.player then
local t = minetest.explode_textlist_event(fields.player)
if (t.type == "CHG") or (t.type == "DCL") then
state.player_index = t.index
minetest.show_formspec(name, FORMNAME, make_fs(name))
end
return
end
if fields.entry then
local t = minetest.explode_textlist_event(fields.entry)
if (t.type == "CHG") or (t.type == "DCL") then
state.entry_index = t.index
minetest.show_formspec(name, FORMNAME, make_fs(name))
end
return
end
if fields.search then
local filter = fields.filter or ""
state.filter = filter
state.list = make_list(filter)
minetest.show_formspec(name, FORMNAME, make_fs(name))
end
end)
minetest.register_chatcommand("xban_gui", {
description = "Show XBan GUI",
params = "",
privs = { ban=true, },
func = function(name, params)
minetest.show_formspec(name, FORMNAME, make_fs(name))
end,
})

View File

@ -1,29 +0,0 @@
function xban.importers.minetest()
local f, e = io.open(minetest.get_worldpath().."/ipban.txt")
if not f then
return false, "Unable to open `ipban.txt': "..e
end
for line in f:lines() do
local ip, name = line:match("([^|]+)%|(.+)")
if ip and name then
local entry
entry = xban.find_entry(ip, true)
entry.banned = true
entry.reason = "Banned in `ipban.txt'"
entry.names[name] = true
entry.names[ip] = true
entry.time = os.time()
entry.expires = nil
entry.source = "xban:importer_minetest"
table.insert(entry.record, {
source = entry.source,
reason = entry.reason,
time = entry.time,
expires = nil,
})
end
end
f:close()
return true
end

View File

@ -1,33 +0,0 @@
function xban.importers.v1()
local f, e = io.open(minetest.get_worldpath().."/players.iplist")
if not f then
return false, "Unable to open `players.iplist': "..e
end
for line in f:lines() do
local list = line:split("|")
if #list >= 2 then
local banned = (list[1]:sub(1, 1) == "!")
local entry
entry = xban.find_entry(list[1], true)
entry.banned = banned
for _, name in ipairs(list) do
entry.names[name] = true
end
if banned then
entry.reason = "Banned in `players.iplist'"
entry.time = os.time()
entry.expires = nil
entry.source = "xban:importer_v1"
table.insert(entry.record, {
source = entry.source,
reason = entry.reason,
time = entry.time,
expires = nil,
})
end
end
end
f:close()
return true
end

View File

@ -1,35 +0,0 @@
function xban.importers.v2()
return pcall(function()
local f, e = io.open(minetest.get_worldpath().."/players.iplist.v2")
if not f then
error("Unable to open `players.iplist.v2': "..e)
end
local text = f:read("*a")
f:close()
local db = minetest.deserialize(text)
for _, ent in ipairs(db) do
for name in pairs(ent.names) do
local entry = xban.find_entry(name, true)
if entry.source ~= "xban:importer_v2" then
for nm in pairs(e.names) do
entry.names[nm] = true
end
if ent.banned then
entry.banned = true
entry.reason = e.banned
entry.source = "xban:importer_v2"
entry.time = ent.time
entry.expires = ent.expires
table.insert(entry.record, {
source = entry.source,
reason = entry.reason,
time = entry.time,
expires = entry.expires,
})
end
end
end
end
end)
end

View File

@ -1,418 +0,0 @@
xban = { MP = minetest.get_modpath(minetest.get_current_modname()) }
dofile(xban.MP.."/serialize.lua")
local db = { }
local tempbans = { }
local DEF_SAVE_INTERVAL = 300 -- 5 minutes
local DEF_DB_FILENAME = minetest.get_worldpath().."/xban.db"
local DB_FILENAME = minetest.settings:get("xban.db_filename")
local SAVE_INTERVAL = tonumber(
minetest.settings:get("xban.db_save_interval")) or DEF_SAVE_INTERVAL
if (not DB_FILENAME) or (DB_FILENAME == "") then
DB_FILENAME = DEF_DB_FILENAME
end
local function make_logger(level)
return function(text, ...)
minetest.log(level, "[xban] "..text:format(...))
end
end
local ACTION = make_logger("action")
local WARNING = make_logger("warning")
local unit_to_secs = {
s = 1, m = 60, h = 3600,
D = 86400, W = 604800, M = 2592000, Y = 31104000,
[""] = 1,
}
local function parse_time(t) --> secs
local secs = 0
for num, unit in t:gmatch("(%d+)([smhDWMY]?)") do
secs = secs + (tonumber(num) * (unit_to_secs[unit] or 1))
end
return secs
end
local function concat_keys(t, sep)
local keys = {}
for k, _ in pairs(t) do
keys[#keys + 1] = k
end
return table.concat(keys, sep)
end
function xban.find_entry(player, create) --> entry, index
for index, e in ipairs(db) do
for name in pairs(e.names) do
if name == player then
return e, index
end
end
end
if create then
print(("Created new entry for `%s'"):format(player))
local e = {
names = { [player]=true },
banned = false,
record = { },
}
table.insert(db, e)
return e, #db
end
return nil
end
function xban.get_info(player) --> ip_name_list, banned, last_record
local e = xban.find_entry(player)
if not e then
return nil, "No such entry"
end
return e.names, e.banned, e.record[#e.record]
end
function xban.ban_player(player, source, expires, reason) --> bool, err
if xban.get_whitelist(player) then
return nil, "Player is whitelisted; remove from whitelist first"
end
local e = xban.find_entry(player, true)
if e.banned then
return nil, "Already banned"
end
local rec = {
source = source,
time = os.time(),
expires = expires,
reason = reason,
}
table.insert(e.record, rec)
e.names[player] = true
local pl = minetest.get_player_by_name(player)
if pl then
local ip = minetest.get_player_ip(player)
if ip then
e.names[ip] = true
end
e.last_pos = pl:getpos()
end
e.reason = reason
e.time = rec.time
e.expires = expires
e.banned = true
local msg
local date = (expires and os.date("%c", expires)
or "the end of time")
if expires then
table.insert(tempbans, e)
msg = ("Banned: Expires: %s, Reason: %s"):format(date, reason)
else
msg = ("Banned: Reason: %s"):format(reason)
end
for nm in pairs(e.names) do
minetest.kick_player(nm, msg)
end
ACTION("%s bans %s until %s for reason: %s", source, player,
date, reason)
ACTION("Banned Names/IPs: %s", concat_keys(e.names, ", "))
return true
end
function xban.unban_player(player, source) --> bool, err
local e = xban.find_entry(player)
if not e then
return nil, "No such entry"
end
local rec = {
source = source,
time = os.time(),
reason = "Unbanned",
}
table.insert(e.record, rec)
e.banned = false
e.reason = nil
e.expires = nil
e.time = nil
ACTION("%s unbans %s", source, player)
ACTION("Unbanned Names/IPs: %s", concat_keys(e.names, ", "))
return true
end
function xban.get_whitelist(name_or_ip)
return db.whitelist and db.whitelist[name_or_ip]
end
function xban.remove_whitelist(name_or_ip)
if db.whitelist then
db.whitelist[name_or_ip] = nil
end
end
function xban.add_whitelist(name_or_ip, source)
local wl = db.whitelist
if not wl then
wl = { }
db.whitelist = wl
end
wl[name_or_ip] = {
source=source,
}
return true
end
function xban.get_record(player)
local e = xban.find_entry(player)
if not e then
return nil, ("No entry for `%s'"):format(player)
elseif (not e.record) or (#e.record == 0) then
return nil, ("`%s' has no ban records"):format(player)
end
local record = { }
for _, rec in ipairs(e.record) do
local msg = rec.reason or "No reason given."
if rec.expires then
msg = msg..(", Expires: %s"):format(os.date("%c", e.expires))
end
if rec.source then
msg = msg..", Source: "..rec.source
end
table.insert(record, ("[%s]: %s"):format(os.date("%c", e.time), msg))
end
local last_pos
if e.last_pos then
last_pos = ("User was last seen at %s"):format(
minetest.pos_to_string(e.last_pos))
end
return record, last_pos
end
minetest.register_on_prejoinplayer(function(name, ip)
local wl = db.whitelist or { }
if wl[name] or wl[ip] then return end
local e = xban.find_entry(name) or xban.find_entry(ip)
if not e then return end
if e.banned then
local date = (e.expires and os.date("%c", e.expires)
or "the end of time")
return ("Banned: Expires: %s, Reason: %s"):format(
date, e.reason)
end
end)
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
local e = xban.find_entry(name)
local ip = minetest.get_player_ip(name)
if not e then
if ip then
e = xban.find_entry(ip, true)
else
return
end
end
e.names[name] = true
if ip then
e.names[ip] = true
end
e.last_seen = os.time()
end)
minetest.register_chatcommand("xban", {
description = "XBan a player",
params = "<player> <reason>",
privs = { ban=true },
func = function(name, params)
local plname, reason = params:match("(%S+)%s+(.+)")
if not (plname and reason) then
return false, "Usage: /xban <player> <reason>"
end
local ok, e = xban.ban_player(plname, name, nil, reason)
return ok, ok and ("Banned %s."):format(plname) or e
end,
})
minetest.register_chatcommand("xtempban", {
description = "XBan a player temporarily",
params = "<player> <time> <reason>",
privs = { ban=true },
func = function(name, params)
local plname, time, reason = params:match("(%S+)%s+(%S+)%s+(.+)")
if not (plname and time and reason) then
return false, "Usage: /xtempban <player> <time> <reason>"
end
time = parse_time(time)
if time < 60 then
return false, "You must ban for at least 60 seconds."
end
local expires = os.time() + time
local ok, e = xban.ban_player(plname, name, expires, reason)
return ok, (ok and ("Banned %s until %s."):format(
plname, os.date("%c", expires)) or e)
end,
})
minetest.register_chatcommand("xunban", {
description = "XUnBan a player",
params = "<player_or_ip>",
privs = { ban=true },
func = function(name, params)
local plname = params:match("%S+")
if not plname then
minetest.chat_send_player(name,
"Usage: /xunban <player_or_ip>")
return
end
local ok, e = xban.unban_player(plname, name)
return ok, ok and ("Unbanned %s."):format(plname) or e
end,
})
minetest.register_chatcommand("xban_record", {
description = "Show the ban records of a player",
params = "<player_or_ip>",
privs = { ban=true },
func = function(name, params)
local plname = params:match("%S+")
if not plname then
return false, "Usage: /xban_record <player_or_ip>"
end
local record, last_pos = xban.get_record(plname)
if not record then
local err = last_pos
minetest.chat_send_player(name, "[xban] "..err)
return
end
for _, e in ipairs(record) do
minetest.chat_send_player(name, "[xban] "..e)
end
if last_pos then
minetest.chat_send_player(name, "[xban] "..last_pos)
end
return true, "Record listed."
end,
})
minetest.register_chatcommand("xban_wl", {
description = "Manages the whitelist",
params = "(add|del|get) <name_or_ip>",
privs = { ban=true },
func = function(name, params)
local cmd, plname = params:match("%s*(%S+)%s*(%S+)")
if cmd == "add" then
xban.add_whitelist(plname, name)
ACTION("%s adds %s to whitelist", name, plname)
return true, "Added to whitelist: "..plname
elseif cmd == "del" then
xban.remove_whitelist(plname)
ACTION("%s removes %s to whitelist", name, plname)
return true, "Removed from whitelist: "..plname
elseif cmd == "get" then
local e = xban.get_whitelist(plname)
if e then
return true, "Source: "..(e.source or "Unknown")
else
return true, "No whitelist for: "..plname
end
end
end,
})
local function check_temp_bans()
minetest.after(60, check_temp_bans)
local to_rm = { }
local now = os.time()
for i, e in ipairs(tempbans) do
if e.expires and (e.expires <= now) then
table.insert(to_rm, i)
e.banned = false
e.expires = nil
e.reason = nil
e.time = nil
end
end
for _, i in ipairs(to_rm) do
table.remove(tempbans, i)
end
end
local function save_db()
minetest.after(SAVE_INTERVAL, save_db)
local f, e = io.open(DB_FILENAME, "wt")
db.timestamp = os.time()
if f then
local ok, err = f:write(xban.serialize(db))
if not ok then
WARNING("Unable to save database: %s", err)
end
else
WARNING("Unable to save database: %s", e)
end
if f then f:close() end
return
end
local function load_db()
local f, e = io.open(DB_FILENAME, "rt")
if not f then
WARNING("Unable to load database: %s", e)
return
end
local cont = f:read("*a")
if not cont then
WARNING("Unable to load database: %s", "Read failed")
return
end
local t, e2 = minetest.deserialize(cont)
if not t then
WARNING("Unable to load database: %s",
"Deserialization failed: "..(e2 or "unknown error"))
return
end
db = t
tempbans = { }
for _, entry in ipairs(db) do
if entry.banned and entry.expires then
table.insert(tempbans, entry)
end
end
end
minetest.register_chatcommand("xban_cleanup", {
description = "Removes all non-banned entries from the xban db",
privs = { server=true },
func = function(name, params)
local old_count = #db
local i = 1
while i <= #db do
if not db[i].banned then
-- not banned, remove from db
table.remove(db, i)
else
-- banned, hold entry back
i = i + 1
end
end
-- save immediately
save_db()
return true, "Removed " .. (old_count - #db) .. " entries, new db entry-count: " .. #db
end,
})
minetest.register_on_shutdown(save_db)
minetest.after(SAVE_INTERVAL, save_db)
load_db()
xban.db = db
minetest.after(1, check_temp_bans)
dofile(xban.MP.."/dbimport.lua")
dofile(xban.MP.."/gui.lua")

View File

@ -1 +0,0 @@
name = xban2

View File

@ -1,31 +0,0 @@
local function repr(x)
if type(x) == "string" then
return ("%q"):format(x)
else
return tostring(x)
end
end
local function my_serialize_2(t, level)
level = level or 0
local lines = { }
local indent = ("\t"):rep(level)
for k, v in pairs(t) do
local typ = type(v)
if typ == "table" then
table.insert(lines,
indent..("[%s] = {\n"):format(repr(k))
..my_serialize_2(v, level + 1).."\n"
..indent.."},")
else
table.insert(lines,
indent..("[%s] = %s,"):format(repr(k), repr(v)))
end
end
return table.concat(lines, "\n")
end
function xban.serialize(t)
return "return {\n"..my_serialize_2(t, 1).."\n}"
end