diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..8fe57ee83 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,23 @@ +--- +name: Bug report +about: Report an error or something that doesn't work correcty +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +Describe what happened and what you were trying to do. + +**Reproduction steps** +Describe the context of the bug. Did it happen during a match, in the lounge or out of an arena? If it was during a match, with which goal and how many teams/players? Is there a specific action to do in order to trigger the error? + +**Additional elements** +If applicable, add screenshots, log extract (between two lines of ` ``` `), or additional information to help explain your problem. + +**Config information** +- Server software and version: [e.g. Paper 1.20.4] +- Java Version: [e.g. 17] +- PVPArena version: [e.g. 1.15.4-SNAPSHOT-b1] +- [If necessary: affected module and its version] diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..a6016b329 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,11 @@ +--- +name: Feature request +about: Suggest an idea for enhancing PVPArena +title: '' +labels: enhancement +assignees: '' + +--- + +**Describe the solution you'd like** +A clear and concise description of what you want and why. diff --git a/.gitignore b/.gitignore index c76adf957..36779000f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ dependency-reduced-pom.xml /bin/ -/.settings/ \ No newline at end of file +/.settings/ +/.classpath +/.project diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/doc/commands.md b/doc/commands.md index a904352e8..7daf7177b 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -19,12 +19,13 @@ Click on a command to get its syntax, usage examples and more information about Command | Shorthand | Definition ------------- | ------------- | ------------- [/pa debug](commands/debug.md) | /pa !d | Debug nodes +/pa duty | /pa !du | Toggle your shortcuts override status [/pa modules](commands/modules.md) | /pa !mi | Manage modules [/pa reload](commands/reload.md) | /pa !r | Reload arena configs ## Arena Administration Commands -> ℹ Permission: pvparena.admin OR both ownership and pvparena.create +> ℹ Permission: pvparena.admin OR both ownership of an arena and pvparena.create Command | Shorthand | Definition ------------- | ------------- | ------------- @@ -39,6 +40,7 @@ Command | Shorthand | Definition [/pa forcewin](commands/forcewin.md) | /pa !fw | Force a player/team to win. [/pa gamemode](commands/gamemode.md) | /pa !gm | Change the general gamemode of an arena [/pa goal](commands/goal.md) | /pa !g | Manage arena goals +[/pa playerclass](commands/playerclass.md) | /pa !pcl | Manage player classes [/pa playerjoin](commands/playerjoin.md) | /pa !pj | Make a player join [/pa protection](commands/protection.md) | /pa !p | Manage arena protections [/pa region](commands/region.md) | /pa !rg | Manage arena regions @@ -75,4 +77,4 @@ Command | Shorthand | Definition [/pa ready](commands/ready.md) | /pa -r | Ready you up or list who is ready /pa spectate | /pa -s | Spectate an arena [/pa stats](commands/stats.md) | /pa -s | Show [arena/global] statistics -/pa version | /pa -v | Show detailed version information \ No newline at end of file +/pa version | /pa -v | Show detailed version information diff --git a/doc/commands/playerclass.md b/doc/commands/playerclass.md new file mode 100644 index 000000000..8505a4856 --- /dev/null +++ b/doc/commands/playerclass.md @@ -0,0 +1,21 @@ +# Playerclass command + +## Description + +This command manages the player class for an arena. You can use it to edit and remove (clear) it, but you cannot show it. + +## Usage + +Command | Definition +------------- | ------------- +/pa [arena] playerclass save | Save your inventory to the class items of your player class +/pa [arena] playerclass remove | Remove a player class + +Example: use `/pa temp playerclass save` to save your inventory to your player class of the arena "temp" + +> **🚩 Tip:** +> Type `/pa leave` to leave class preview + +## Hazards + +You shouldn't do that when a game is running, and make sure that you /pa reload afterwards. diff --git a/doc/commands/setowner.md b/doc/commands/setowner.md index 117c1eaf2..7a0149af3 100644 --- a/doc/commands/setowner.md +++ b/doc/commands/setowner.md @@ -1,11 +1,11 @@ # Setowner Command -##Description +## Description -This command hands over ownership. This has to be either "%server%" or a player name. Note that the player name is +This command hands over ownership. This has to be either "%server%" or a player name. By default every arena created by a player with permission `pvparena.admin` is owned by the server. Note that the player name is NOT checked, but you can verify it with [`/pa info`](../commands.md#arena-standard-commands) -##Usage Examples +## Usage Examples Command | Definition ------------- | ------------- diff --git a/doc/commands/spawn.md b/doc/commands/spawn.md index 1766a775d..b99d62012 100644 --- a/doc/commands/spawn.md +++ b/doc/commands/spawn.md @@ -17,12 +17,18 @@ Example: ## Details -There are two syntax according to the [gamemode](gamemode.md) of your arena : -- If you're using a "free" arena, you can define unlimited spawns using syntax `/pa myArena spawn spawnX` where X should - be anything (word, digit, letter, etc). -- If your arena works with teams, you have to use `/pa myArena spawn teamspawn` where "team" is the name of one of your -team. +There are two syntaxes for the spawns for the fight, depending on the [gamemode](gamemode.md) of your arena: +- if you're using a "free" arena, you can define unlimited spawns using syntax `/pa myArena spawn spawnX` where X can + be anything (word, digit, letter, etc.) +- if your arena works with teams, you have to use `/pa myArena spawn teamspawn` where "team" is the name of one of your +teams +Then you must have lounge spawns: +- in the "free" gamemode: just one spawn named `lounge` +- in the "team" gamemode: one spawn for each team with the syntax `teamlounge` where "team" is the name of the +team + +In addition to these spawns, every arena must have "exit" and "spectator" spawns. If you get a message "spawn unknown", this is probably because you did not install/activate a [goal](../goals.md) or a [module](../modules.md). @@ -36,4 +42,4 @@ You can define unique offsets for each spawn name, in order to not be placed on - `/pa [arena] spawn [spawnname] offset X Y Z` For example 0.5 0 0.5 as X Y Z would work setting you on an edge. -You might want to keep F3 at hand to see if you actually have to add or subtract to get to the right edge. \ No newline at end of file +You might want to keep F3 at hand to see if you actually have to add or subtract to get to the right edge. diff --git a/doc/configuration.md b/doc/configuration.md index 924ff6a3d..2eaca2a6c 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -34,7 +34,7 @@ general: leavedeath: false #Kill the player on battleground leaving lang: none owner: server #Set owner of the arena - regionclearexceptions: [] #List of regions where entities are not cleared + regionclearexceptions: [] #List of entities that must not be cleared from arena regions, editable with '/pa regionclear' command quickspawn: true #Spawn all players at the same time. If false, spawn player one by one. prefix: MyArena #Name of the arena displayed in chat messages showRemainingLives: true #Brodcast ramaning lives in chat @@ -53,6 +53,7 @@ goal: items: keepAllOnRespawn: false #Keep inventory on respawn excludeFromDrops: none #List of items not dropped on kill + onlyDrops: none #List of only items dropped on kill keepOnRespawn: none #List of items kept on respawn (if keepAllOnRespawn is disabled) minplayers: 2 #Minimum number of players to start fighting random: true @@ -201,4 +202,4 @@ spawns: spawn2: world,863,58,-1007,0,0 spawn3: world,853,58,-997,-90,0 spawn4: world,863,58,-987,180,0 -``` \ No newline at end of file +``` diff --git a/doc/faq.md b/doc/faq.md index 1585f4211..c327d5c06 100644 --- a/doc/faq.md +++ b/doc/faq.md @@ -44,6 +44,21 @@ an arena, use the command `sudo @p pa leave`.
+## How to create a join sign for an arena? + +Create a simple sign with the following pattern: +``` +[arena] +yourArenaName +teamName + +``` + +You can keep the third line empty to join a random team. +The fourth line can still be empty or filled with a custom message. + +
+ ## How to regen my battlefield after a game? Currently, there are two ways to regen battlefield after a match. You can use either @@ -70,6 +85,19 @@ Just set `tp.death` to `spectator` in your arena config file (or with [`/pa [are
+## Is it possible to automatically affect a class to all players or to a specific team? + +Yes it is. In your arena config, you can set the `autoclass` the setting according to your needs: +* Use `None` if you don't want to use the auto-class mechanism. (default option) +* Write a simple class name, to affect the class to everyone. + Ex: `autoclass: pyro` +* Use the following pattern to affect a class to each team: + `autoclass: teamName1:classNameA;teamName2:classNameB` + +NB: For the 3rd option, you have to specify a class for each team. There is no default choice. + +
+ ## Still have questions? Don't hesitate to [get in touch](../readme.md#support) with us 😉 diff --git a/doc/mods/betterfight.md b/doc/mods/betterfight.md index 19539bd7e..d5ce46af2 100644 --- a/doc/mods/betterfight.md +++ b/doc/mods/betterfight.md @@ -28,7 +28,7 @@ Case does not matter :) ## Commands -- `/pa [arena] !bf messages [number]` \- set message for [number]th kill +- `/pa [arena] !bf messages [number] [message]` \- set message for [number]th kill - `/pa [arena] !bf items [items]` \- set an item string to add deadly items ("fireball,snowball,arrow") - `/pa [arena] !bf reset` \- toggle killstreakondeath reset diff --git a/doc/mods/items.md b/doc/mods/items.md index 88f6e0c9e..7ca7f2d91 100644 --- a/doc/mods/items.md +++ b/doc/mods/items.md @@ -22,6 +22,8 @@ Now set as many item spawning places as you want: `/pa [arena] spawn itemX` - items \- the items to choose from - interval \- spawn interval in seconds +> 🚩 You can set items with the [set](https://github.com/Eredrim/pvparena/blob/master/doc/commands/set.md) command (`inventory` or `hand`) + ## Commands \- diff --git a/doc/mods/powerups.md b/doc/mods/powerups.md index 88e8fb59b..c1d850d16 100644 --- a/doc/mods/powerups.md +++ b/doc/mods/powerups.md @@ -17,98 +17,98 @@ Sorry, but you have to add a freaking block to your arena config under `module.p ```yaml items: -- Shield: - - item: OBSIDIAN - - dmg_receive: - - factor: 0.6 -- Minions: - - item: BONE - - spawn_mob: - - type: skeleton - - duration: 10 -- Sprint: - - item: FEATHER - - sprint: - - duration: 10 -- QuadDamage: - - item: IRON_INGOT - - dmg_cause: - - factor: 4.0 - - duration: 10 -- Dodge: - - item: IRON_DOOR - - dmg_receive: - - chance: 0.2 - - factor: 0.0 - - duration: 5 -- Reflect: - - item: WOOD_DOOR - - dmg_reflect: - - chance: 0.5 - - factor: 0.3 - - uses: 5 -- Ignite: - - item: FLINT_AND_STEEL - - ignite: - - chance: 0.66 - - duration: 10 -- IceBlock: - - item: ICE - - freeze: - - factor: 0.0 - - duration: 8 - - dmg_receive: - - factor: 0.0 - - duration: 8 -- Invulnerability: - - item: EGG - - dmg_receive: - - factor: 0.0 - - duration: 5 -- OneUp: - - item: BROWN_MUSHROOM - - lives: - - diff: 1 -- Death: - - item: RED_MUSHROOM - - lives: - - diff: -1 -- Slippery: - - item: WATER_BUCKET - - slip: - - duration: 10 -- Dizzyness: - - item: COMPASS - - portal: - - duration: 10 -- Rage: - - item: ROTTEN_FLESH - - dmg_cause: - - factor: 0.0 - - chance: 0.2 - - duration: 5 -- Berserk: - - item: CACTUS - - dmg_cause: - - factor: 1.5 - - duration: 5 - - dmg_receive: - - dactor: 1.5 - - duration: 5 -- Healing: - - item: APPLE - - heal: - - factor: 1.5 - - duration: 10 -- Heal: - - item: BREAD - - health: - - diff: 3 -- Repair: - - item: WORKBENCH - - repair: - - items: helmet,chestplate,leggins,boots - - factor: 0.2 + Shield: + item: OBSIDIAN + dmg_receive: + factor: 0.6 + Minions: + item: BONE + spawn_mob: + type: skeleton + duration: 10 + Sprint: + item: FEATHER + sprint: + duration: 10 + QuadDamage: + item: IRON_INGOT + dmg_cause: + factor: 4.0 + duration: 10 + Dodge: + item: IRON_DOOR + dmg_receive: + chance: 0.2 + factor: 0.0 + duration: 5 + Reflect: + item: OAK_DOOR + dmg_reflect: + chance: 0.5 + factor: 0.3 + uses: 5 + Ignite: + item: FLINT_AND_STEEL + ignite: + chance: 0.66 + duration: 10 + IceBlock: + item: ICE + freeze: + factor: 0.0 + duration: 8 + dmg_receive: + factor: 0.0 + duration: 8 + Invulnerability: + item: EGG + dmg_receive: + factor: 0.0 + duration: 5 + OneUp: + item: BROWN_MUSHROOM + lives: + diff: 1 + Death: + item: RED_MUSHROOM + lives: + diff: -1 + Slippery: + item: WATER_BUCKET + slip: + duration: 10 + Dizzyness: + item: COMPASS + portal: + duration: 10 + Rage: + item: ROTTEN_FLESH + dmg_cause: + factor: 0.0 + chance: 0.2 + duration: 5 + Berserk: + item: CACTUS + dmg_cause: + factor: 1.5 + duration: 5 + dmg_receive: + factor: 1.5 + duration: 5 + Healing: + item: APPLE + heal: + factor: 1.5 + duration: 10 + Heal: + item: BREAD + health: + diff: 3 + Repair: + item: CRAFTING_TABLE + repair: + items: helmet,chestplate,leggins,boots + factor: 0.2 ``` So the first layer defines the name, the second layer defines item and adds all the effects it has. This example features all possible ways of doing good and bad things, I hope it is clear oO diff --git a/doc/mods/worldedit.md b/doc/mods/worldedit.md index 5e5c3c133..4b2b17916 100644 --- a/doc/mods/worldedit.md +++ b/doc/mods/worldedit.md @@ -12,7 +12,7 @@ This module needs a full server restart to hook into WorldEdit properly, for the ## Config settings -*These settings can be found under `mods.worldedit` node in your arena config file.* +*These settings can be found under `modules.worldedit` node in your arena config file.* - autoload - automatically load the arena's BATTLE regions after fight (default: false) - autosave - automatically save the arena's BATTLE regions before fight (default: false) diff --git a/doc/mods/worldguard.md b/doc/mods/worldguard.md index b243d90cb..0857735a5 100644 --- a/doc/mods/worldguard.md +++ b/doc/mods/worldguard.md @@ -1,32 +1,15 @@ -# Squads +# Worldguard ## Description -This mod adds squads to the game, basically only showing players belonging together apart from teams and classes. - -## Installation - -Unzip the module files (files tab, "PA Files v*.*.*") into the /pvparena/files folder and install them via - -- `/pa modules install [modname]`, activate per arena via -- `/pa [arenaname] !tm [modname]` - -## Setup - -\- - -## Config settings ( config.yml !!! NOT per arena! ) - -- modules.squads.ingameSquadSwitch \- allow switching squads ingame +This mod adds creates or updates an arena region based on the coordinates of a worldguard region. ## Commands -- `/pa wgload [regionname] [wgregionname]` \- load WorldGuard region wgregionname to PVP Arena region regionname - -## Warnings +- `/pa wgload [regionname] [wgregionname]` \- load coordinates of WorldGuard region *wgregionname* to PVP Arena region *regionname*. -\- +If *wgregionname* coordinates have been changed, you can type this command again to update *regionname* coordinates. ## Dependencies -- WorldGuard 5.9.1 +- WorldGuard 7.0+ diff --git a/doc/modules.md b/doc/modules.md index 3cfd23f7e..96e2b4914 100644 --- a/doc/modules.md +++ b/doc/modules.md @@ -77,7 +77,7 @@ if you encounter one, you can [report it](https://github.com/Eredrim/pvparena/is > ℹ This has to be done only once Use the [`/pa modules download`](commands/modules.md) command to download the release version of modules. If you want to -install a dev build version, download the zip archive on [jenkins](https://ci.craftyn.com/job/PVP%20Arena%20Modules/) +install a dev build version, download the zip archive directly on our [discord](https://discord.gg/a8NhSsXKVQ) and deflate it in the `/files` directory of pvparena. After this step, if you type [`/pa modules list`](commands/modules.md), you will show the list of all installable @@ -96,4 +96,5 @@ Type [`/pa modules install [moduleName]`](commands/modules.md) to install one of > ℹ This has to be done for each arena Last step: your module is installed and you want to use it in some of your arenas. -Type [`/pa [arena] !tm [moduleName]`](commands/togglemod.md) to enable it in your arena. \ No newline at end of file +Type [`/pa [arena] !tm [moduleName]`](commands/togglemod.md) to enable it in your arena. +Then just type [`/pa [arena] reload`](commands/reload.md) to apply module settings in configuration file. diff --git a/doc/permissions.md b/doc/permissions.md index c9eb4f9af..d9f79d091 100644 --- a/doc/permissions.md +++ b/doc/permissions.md @@ -7,13 +7,15 @@ Node | Definition pvparena.* | Gives access to all commands pvparena.admin | Allows you to create and administrate arenas (default: op) pvparena.create| Allows you to create and administrate your arenas (default: op) +pvparena.override| Allows you to override some shortcuts checks (default: op) pvparena.telepass| Allows you to teleport while in an arena (default: op) -pvparena.user | Allows you to use the arena (default: true) +pvparena.user | Allows you to use the arena (default: everyone) +PVP Arena uses the SuperPerms interface, i.e. default bukkit permissions interface. ### Specific class permissions -If you activate `explicitClassNeeded` you have to add permissions e.g. (proper class name case !!) +If you activate `explicitClassNeeded` you have to add permissions e.g. (proper class name case!) Node | Definition ------------- | ------------- @@ -29,4 +31,12 @@ Node | Definition pvparena.join.ctf | Give ctf join permission pvparena.join.spleef | Give spleef join permission -PVP Arena uses the SuperPerms interface, i.e. default bukkit permissions interface. \ No newline at end of file + +### Specific command permissions + +You can allow regular, non-op players to use specific commands. Negating these permissions won't work. Examples: + +Node | Definition +------------- | ------------- +pvparena.cmds.playerjoin | Allows player to run [/pvparena playerjoin](commands/playerjoin.md) +pvparena.cmds.spawn | Allows player to modify [spawns](commands/spawn.md) of the arenas diff --git a/lang/lang_en.yml b/lang/lang_en.yml index 02d6902f2..9bdb4bedb 100644 --- a/lang/lang_en.yml +++ b/lang/lang_en.yml @@ -110,6 +110,7 @@ nulang: full: The class &a%1%&r is full! notenoughexp: You don't have enough EXP to choose &a%1%&r! notfound: 'Class not found: &a%1%&r' + notgiven: 'No class was given!' cmdblocked: '&cCommand blocked: %1%' invalidcmd: Invalid command (%1%) unknowncmd: Unknown command diff --git a/lang/lang_fr.yml b/lang/lang_fr.yml index d68a0aa4d..81758c3af 100644 --- a/lang/lang_fr.yml +++ b/lang/lang_fr.yml @@ -151,13 +151,13 @@ nulang: nospawns: Pas de spawn enregistré ! classperms: Vous n'avez pas les permissions pour la classe &a%1%&r permjoin: Vous n'avez pas la permission de rejoindre l'arène ! - noperm: '&cPas la permission de %1%' + noperm: '&cVous n''avez pas la permission %1%' noplayerfound: Aucun joueur trouvé! notinarena: Vous n'êtes pas entré dans l'arène ! notnumeric: '&cArgument non numérique:&r %1%' notsameworld: Vous n'êtes pas dans le même mode que l'arène (%1%)! noteamfound: Aucune équipe trouvée! - onlyplayers: '&cCette comande ne peut être utilisée que par des joueurs!' + onlyplayers: '&cCette commande ne peut être utilisée que par des joueurs!' playernotfound: '&cJoueur non trouvé: &f%1%&c!' positives: 'Valeur positive: &b%1%&r' potioneffecttypenotfound: 'Effet de potion non trouvé: &e%1%&r' @@ -198,18 +198,18 @@ nulang: unknownsubcommand: 'Sous-commande inconnue. Commandes valides: &a%1%&r' unknowntype: 'Type inconnu. Types valides: &e%1%&r' nopermto: - madmin: administrer - create: créer une arène - disable: désactiver - edit: éditer une arène - enable: activer - nopermjoin: rejoindre une arène - reload: actualiser - remove: supprimer une arène - set: définir une configuration - setup: configurer une arène - teleport: téléporter au spawn d'une arène - user: utiliser l'arène + madmin: d'administrer + create: de créer une arène + disable: de désactiver une arène + edit: d'éditer une arène + enable: d'activer une arène + nopermjoin: de rejoindre une arène + reload: d'actualiser la config + remove: de supprimer une arène + set: de définir une configuration + setup: de configurer une arène + teleport: de téléporter au spawn d'une arène + user: d'utiliser l'arène cmds: blacklist: d'utiliser la commande blacklist check: d'utiliser la commande check @@ -539,7 +539,7 @@ nulang: ignoreon: Vous ignorez maintenant les annonces ! ignoreoff: Vous recevrez maintenant les annonces ! arenaboards: - createarenaboard: créer un tableau des scores + createarenaboard: de créer un tableau des scores arenaboarddestroyed: tableau des scores détruit ! boardexists: Le tableau des scores existe déjà !' sortingby: Tableau des scores maintenant trié par %1% diff --git a/lang/lang_pl.yml b/lang/lang_pl.yml new file mode 100644 index 000000000..1fd22bee7 --- /dev/null +++ b/lang/lang_pl.yml @@ -0,0 +1,738 @@ +time_intervals: + '1': 1.. + '2': 2.. + '3': 3.. + '4': 4.. + '5': 5.. + '10': 10 %s + '20': 20 %s + '30': 30 %s + '60': 60 %s + '120': 2 %m + '180': 3 %m + '240': 4 %m + '300': 5 %m + '600': 10 %m + '1200': 20 %m + '1800': 30 %m + '2400': 40 %m + '3000': 50 %m + '3600': 60 %m +nulang: + classchest: + done: Successfully set the class items of %1% to the contents of %2%. Please reload + the arena when you are done setting chests! + reloadfail: '&cFail to reload arena %1%.&f Please check the arena config.' + ymls: + reloaded: Languages reloaded! + score: + header: '--- Scores ---' + row: '%1%&r: %2%' + footer: '--------------' + arena: + create: + done: '&7Arena &c%1% &7została stworzona!' + disable: + done: '&7Arena została wyłączona!' + edit: + disabled: '&7Wyłączono tryb edycji dla areny &c%1%' + enabled: '&7Włączono tryb edycji dla areny &c%1%' + enable: + fail: '&cArena failed to load. Please check the config.' + done: '&7Arena została włączona!' + arenalist: '&7Areny: &c%1%&r' + regionshapeunknown: 'Arena Shape ''%1%'' unknown. consult the forums: http://goo.gl/IfLOh' + reload: + done: '&7Arena została przeładowana!' + remove: + done: '&7Arena &c%1%&&7 została usunięta!' + setup: + disabled: '&7Wyłączony tryb konfiguracji dla areny &c%1%' + enabled: '&7Włączony tryb konfiguracji dla areny &c%1%' + startingin: '&7Wystarczająca liczba gotowych zawodników. Zaczynamy za &c%1%&7!' + start: + done: '&7Arena została awaryjnie wystartowana!' + stop: + done: '&7Arena została awaryjnie zatrzymana!' + autosetup: + automanual: Do you want the wizard to be &a%1%&r or &a%2%&r? + automatic: automatic + manual: manual + modeselected: '&7Tryb &c%1%&7 został wybrany!' + welcome: '&7Witamy w kreatorze konfiguracji Areny PVP! &7Proszę wpisać odpowiedzi + na czacie. &cNie potrzebujesz żadnych komend!' + blacklist: + added: '&7Dodano &c%1% &7do&c %2%&7 do czarnej listy !' + allcleared: '&7Czarna lista została wyczyszczona!' + cleared: '&7Czarna lista &c%1%&7 została wyczyszczona!' + help: '&7Użycie: blacklist clear | blacklist [type] [clear|add|remove] [id]' + removed: '&7Usunięto &c%1%&7 od &e%2%&r z czarnej listy!' + show: '&7Czarna lista &c%1%&7:' + check: + done: '&7Kontrola zakończona! Brak błędów!' + class: + list: '&7Dostępne klasy to &c%1%' + preview: '&7Wyświetlany jest teraz podgląd klasy &c%1%' + removed: '&7Klasa &c%1% &czostała usunięta!' + saved: '&7Klasa &c%1%&7 zapisana!' + selected: '&7Przełączyłeś się na klasę &c%1%' + selectedrespawn: '&7Przełączysz się na klasę &c%1%&7 przy następnym respawnie.' + deathcause: + BLOCK_EXPLOSION: an explosion + CONTACT: a cactus + CUSTOM: Herobrine + DROWNING: water + ENTITY_EXPLOSION: an Explosion + FALL: gravity + FIRE_TICK: fire + FIRE: a fire + LAVA: lava + LIGHTNING: Thor + MAGIC: Magical Powers + POISON: Poison + PROJECTILE: something he didn't see coming + STARVATION: hunger + SUFFOCATION: lack of air + SUICIDE: self + THORNS: thorns + VOID: the Void + FALLING_BLOCK: a falling block + HOT_FLOOR: a magma block + CRAMMING: a collision surplus + DRAGON_BREATH: dragon breath + CREEPER: a creeper + SKELETON: a skeleton + SPIDER: a spider + GIANT: a giant + ZOMBIE: a zombie + SLIME: a slime + GHAST: a ghast + PIG_ZOMBIE: a pig zombie + ENDERMAN: an enderman + CAVE_SPIDER: a cave spider + SILVERFISH: silverfish + BLAZE: a blaze + MAGMA_CUBE: a magma cube + ENDER_DRAGON: an ender dragon + WITHER: a wither boss + WITCH: a witch + WOLF: a wolf + IRON_GOLEM: an iron golem + SPLASH_POTION: a splash potion + duty: + 'false': '&7Jesteś teraz po służbie!' + 'true': '&7 Jesteś teraz na służbie!' + error: + moduleupdate: 'You need to set ''update.modules: true'' to use this command!' + nochest: You are not looking at a chest! + noteamavailable: No Team available. + uninstall2: PVP Arena will try to uninstall on server restart! + arena: + alreadyplaying: '&7Jesteś już w tej klasie!' + arenaexists: '&7Arena już działa!' + arenanotexists: '&7Arena &c%1%&7 nie istnieje!' + arenaconfig: '&7Błąd podczas ładowania konfiguracji areny &c%1%' + argumenttype: '&7Nieprawidłowy argument &c%1%&7 nie jest właściwy &c%2%' + argument: '&7Argument nie został rozpoznany:&c %1% &7- Możliwe argumenty:&7 &c%2%' + autosetup: + running: '&7Autosetup jest już uruchomiony! Gracz: &c%1%' + blacklist: + disallowed: '&7Nie możesz tego zrobić!' + unknownsubcommand: '&7Nieznane podpolecenie. Prawidłowe polecenia: &c%1%' + unknowntype: '&7Nieznany typ. Prawidłowe typy: &c%1%' + class: + full: '&7Klasa &c%1%&7 jest pełna!' + notenoughexp: '&7Nie masz wystarczająco dużo EXP, aby wybrać &c%1%' + notfound: '&7Nie znaleziono klasy &c%1%' + cmdblocked: '&cNie możesz tego zrobić! Ta komenda została zablokowana &7(%1%)' + invalidcmd: '&7Nieprawidłowe polecenie! &c(%1%)' + unknowncmd: '&7Nieznana komenda!' + arenadisabled: '&cTa arena została wyłączona! &7Spróbuj ponownie później!' + editmode: '&7Tryb edycji został włączony!' + error: '&cBłąd: %1%' + fightinprogress: '&cWalka jest już w toku!' + goal: + legacyunknown: '&7Cel &c%1%&7 jest nieznany! Dodaj go komendą &c/pa [arena] + goal [goalname]' + goalnotfound: '&7Cel &c%1%&7 nieznany! Cele: &c%2%' + install: '&7Podczas instalacji wystąpił błąd! Błąd: &c%1%&r' + invalid_argument_count: '&cNiewłaściwa liczba argumentów&7 (&c&l%1% &7z&c &l%2%&7)!' + invalidstattype: '&7Nieprawidłowy typ statystyki &c%1%' + valuenotfound: '&7Nieprawidłowa wartość &c%1%&7!' + invfull: '&7Twój ekwipunek był pełny. Nie otrzymałeś wszystkich nagród!' + arenafull: '&cArena jest pełna!' + joinrange: '&7Jesteś zbyt daleko, aby dołączyć do tej areny!' + notjoinregion: '&7Nie jesteś w regionie dołączenia! Przejdź tam, aby dołączyć!' + teamfull: '&7Team &c%1%&7 jest pełen!' + insidevehicle: '&7Nie możesz dołączyć, gdy jesteś w pojeździe!' + errorloungefree: '&7Błąd! Arena nie jest typu wolnego. Użyj ''&c[teamname] lounge&7''' + log: + matnotfound: '&7Nierozpoznany materiał&c %1%' + missingspawn: '&7Brak spawnu &c%1%' + noarenas: '&7Nie znaleziono żadnych aren!' + valueneg: '&7Wartości ujemne &c%1%' + nofight: '&7Nie ma żadnej walki w toku.' + nogoal: '&7Nie dodałeś celu! Użyj &c/pa [arena] goal [goalname]' + nospawns: '&7Spawn nie został ustawiony!' + classperms: '&7Nie masz permisji do dołączenia do klasy &c%1%' + permjoin: '&7Nie masz permisji do dołączenia do areny!' + noperm: '&7Brak uprawnień do %1%' + noplayerfound: '&7Nie znaleziono gracza!' + notinarena: '&7Nie należysz do areny!' + notnumeric: '&7Argument&c %1%&7 nie jest numeryczny!' + notsameworld: '&7Nie jesteś w świecie co arena! (&c%1%&7)' + noteamfound: '&7Nie znaleziono drużyny!' + onlyplayers: '&7Ta komenda może być użyta tylko przez graczy!' + playernotfound: '&7Nie znaleziono gracza &c%1%&7!' + positives: '&7Wartości dodatnie &c%1%&7' + potioneffecttypenotfound: '&7Nie znaleziono PotionEffectType &c%1%' + ready: + notready0: '&cPrzynajmniej jeden zawodnik nie jest gotów!' + notready1: '&cJesteś sam na arenie!' + notready2: '&cTwoja drużyna jest sama na arenie!' + notready3: '&cDrużynie brakuje zawodników!' + notready4: '&cNa arenie brakuje graczy!' + notready5: '&cPrzynajmniej jeden gracz nie wybrał klasy!' + noclass: '&cNie masz klasy!' + error: '&cArena nie jest gotowa! &e%1%' + region: + invalid: Region selection is invalid. Region will have no volume and will be + useless! + beingcreated: '&7Region &c%1%&7 jest już w trakcie tworzenia' + flagnotfound: '&7Flaga regionu &c%1%&7 nieodnaleziona! Poprawna wartość&c %2%' + notfound: '&7Region &c%1%&7 nie został znaleziony!' + protectionnotfound: '&7Ochrona regionu &c%1%&7 nie została odnaleziona!' + typenotfound: '&7Typ regionu &c%1%&7 nieodnaleziony! Poprawna wartość&c %2%' + youselect: '&7Wybierasz już region dla areny!' + youselect2: '&7Wpisz ponownie polecenie, aby anulować tryb wyboru!' + youselectexit: '&7Wybór regionu odwołany!' + regionnotbeingcreated: '&7Region nie jest tworzony!' + regionnotremoved: '&7Nie ma możliwości ustawienia regionu.' + select2: '&7Wybierz dwa punkty przed próbą zapisu.' + setupmode: '&7Tryb ustawień włączony!' + spawn: + unknown: '&7Nieznany spawn &c%1%' + spawnfree: '&7Błąd! Arena jest typu wolnego. Użyj ''spawnX'', gdzie X jest cyfrą + lub literą!' + statsfile: '&7Błąd podczas odczytu pliku statystyk!' + teamnotfound: '&7Team &c%1%&7 nie odnaleziona!' + uninstall: '&7Błąd podczas odinstalowywania &c%1%' + unknownmodule: '&7Moduł &c%1%&7 nieodnaleziony!' + whitelist: + disallowed: '&7Nie wolno Ci tego zrobić!' + unknownsubcommand: '&7Nieznane podpolecenie. Prawidłowe polecenia: &c%1%&r' + unknowntype: '&7Nieznany typ. Prawidłowe typy: &c%1%&r' + nopermto: + madmin: moderowania + create: tworzenia aren + disable: wyłączania + edit: edytowania aren + enable: włączania + nopermjoin: dołączania do areny + reload: reload + remove: usunięcia areny + set: ustawiania konfiguracji + setup: ustawiania areny + teleport: teleportowania do spawna na arenie + user: używania PVP areny + cmds: + uninstall: use the modules command + blacklist: use the blacklist command + check: use the check command + class: use the class command + create: use the create command + debug: use the debug command + disable: use the disable command + duty: use the duty command + edit: use the edit command + enable: use the enable command + gamemode: use the gamemode command + goal: use the goal command + playerclass: use the playerclass command + playerjoin: use the playerjoin command + protection: use the protection command + region: use the region command + regionflag: use the regionflag command + regions: use the regions command + regiontype: use the regiontype command + reload: use the reload command + remove: use the remove command + round: use the round command + set: use the set command + setowner: use the setowner command + setup: use the setup command + spawn: use the spawn command + start: use the start command + stop: use the stop command + teams: use the teams command + teleport: use the teleport command + template: use the template command + togglemod: use the togglemod command + modules: use the modules command + whitelist: use the whitelist command + arenaclass: use the arenaclass command + chat: use the chat command + join: use the join command + leave: use the leave command + spectate: use the spectate command + arenalist: use the arenalist command + help: use the help command + info: use the info command + list: use the list command + ready: use the ready command + shutup: use the shutup command + stats: use the stats command + version: use the version command + fight: + begins: '&cWalka została rozpoczęta!' + draw: '&aTen mecz zakończył się remisem! Nie ma zwycięzców!' + killedbyremaining: '&7Gracz &e%1% &7został zabity przez &e%2%&7! Pozostałe liczby + żyć: &c%3%&7.' + killedbyremainingfrags: '&7Gracz &e%1% &7został zabity przez &e%2%&7! Pozostałe + liczby żyć: &c%3%&7.' + killedbyremainingteam: '&7Gracz &e%1% &7został zabity przez &e%2%&7! Pozostałe + liczby żyć: &c%3%&7.' + killedbyremainingteamfrags: '&7Gracz &e%1% &7został zabity przez &e%2%&7! Pozostałe + liczby żyć: &c%3%&7.' + killedby: '&7Gracz &e%1% &7został zabity przez &e%2%&7!' + playerleft: '&7Gracz &e%1%&7 wyszedł z bitwy!' + forcestop: do zaprzestania walki. + gamemode: + free: '&7Tryb gry free for all ustawiony na arenę &c%1%&7!' + team: '&7Tryb gry został ustawiony na arenie &c%1%&7!' + general: + break: break + place: place + use: use + goal: + checkpoints: + score: '%1% &ereached checkpoint #%2%!' + youmissed: 'You missed checkpoint #%1%! This is #%2%' + added: '&7Cel &c%1%&7 został dodany!' + installing: '&7Nadaj cel za pomocą komendy &c/pa install [goalname]' + removed: '&7Cel &c%1%&7 usunięty!' + blockdestroy: + typeset: '&7Typ bloku ustawiony na: &c%1%' + setflag: '&7Zestaw bloków: &c%1%' + tosetflag: '&7Blok do ustawienia: &c%1%' + dom: + bossbar_claiming: Przejmowanie... + bossbar_unclaiming: Zatracanie... + claiming: '&7Team &c%1%&7 przejmuje flagę!' + claimed: '&7Team &c%1%&7 przejmują flagę!' + score: '&7Drużyna &c%1%&7 zdobyła &c%2%&7 punktów dzięki utrzymaniu flagi!' + contesting: '&7Flaga zgłoszona przez drużynę &c%1%&7 jest kwestionowana!' + unclaiming: '&7Flaga zgłoszona przez zespół &c%1%&7 nie została odebrana!' + unclaimingby: '&7Flaga, do której rości sobie prawo drużyna &c%1%&7, nie jest + odzyskiwana przez drużynę&c %2%&7!' + infected: + lost: '&aZainfekowani gracze zostali zabici!' + player: '&7Gracz &c%1%&7 jest zainfekowany!' + you: '&7Jesteś zainfekowany!' + won: '&cZainfekowani gracze wygrali grę!' + iprotect: '&7Jesteś zainfekowany! Nie możesz tego zrobić!' + iprotectset: '&7Ochrona graczu &c%1%&7 ustawiona na &c%2%' + killreward: + added: '&7Dodano nagrodę za zabicie: &e%1%&f->&a%2%' + removed: '&7Usunięto nagrodę za zabicie' + liberation: + scoreboardheading: 'Players in jail:' + scoreboardseparator: '----------------' + liberated: '&7Drużyna %1% została uwolniona.!' + setbutton: 'Button set: %1%' + tosetbutton: 'Button to set: %1%' + physicalflags: + holdflag: You have to hold the flag to bring it back! + sabotage: + youcannotselfdestroy: You can not ignite your own TNT!' + tntignite: '%1% ignited the TNT of team %2%!' + set: 'TNT set: %1%' + toset: 'TNT to set: %1%' + noselfdestroy: You can not ignite your own TNT! + youtnt: You now carry the sabotage tool! + notgooditem: You need sabotage tool to ignite the TNT. + tank: + tankdown: The tank is down! + tankmode: TANK MODE! Everyone kill %1%, the tank! + tankwon: The tank has won! Congratulations to %1%! + tdc: + denied: '%1% denied a kill!' + remaining: '%1% kills remaining for %2%.' + scored: '%1% scored a kill!' + youdenied: You denied a kill! + youscored: You scored a kill! + pillars: + msg: + block_broken: '[%1%] %2% broke a block!' + lower: '[%1%] %2% shortened the pillar!' + block_placed: '[%1%] %2% placed a block!' + higher: '[%1%] %2% lengthened the pillar!' + claimed: '[%1%] %2% claimed the pillar!' + unclaimed: '[%1%] %2% unclaimed the pillar!' + score: '%1% scored %2% points' + help: + head: '&e--- &aPVP Arena Help&e %1% &e---' + admin: '&c%1% - help administrating' + setup: '&e%1% - help setting up' + custom: '&e%1% - help customizing' + game: '&a%1% - help ingame' + info: '&9%1% - help getting information' + import: + done: Successfully imported arena &e%1%&r! + info: + classes: 'Classes: &a%1%&r' + goal_active: 'Goal: &a%1%&r' + goal_inactive: 'Goal: &b%1%&r &7== INACTIVE ==' + head_headlin: 'Arena Information about: &a%1%&r | [&a%2%&r]' + head_teams: 'Teams: &a%1%&r' + mod_active: 'Module: &a%1%&r' + mod_inactive: 'Module: &b%1%&r &7== INACTIVE ==' + owner: 'Owner: &a%1%&r' + regions: 'Regions: &a%1%&r' + section: '----- &a%1%&r -----' + install: + installed: 'installed: &a%1%&r' + list: + arenas: 'Available arenas: %1%' + dead: 'Dead: %1%' + fighting: 'Fighting: %1%' + lost: 'Lost: %1%' + lounge: 'Lounge: %1%' + 'null': 'Glitched: %1%' + players: 'Players: %1%' + team: 'Team %1%: %2%' + ready: 'Ready: %1%' + warm: 'Warm: %1%' + watching: 'Watching: %1%' + log: + plugindisabled: disabled (version %1%) + pluginenabled: enabled (version %1%) + trickerdisabled: Plugin tracking disabled. See you soon? + trackingenabled: 'Plugin tracking enabled. Set tracker: false inside the main + config to disable.' + updatedisabled: Updates deactivated. Please check dev.bukkit for updates. + updateenabled: Checking for updates... + warning: '%1%' + messages: + toArena: You are now talking to the arena! + toPublic: You are now talking to the public! + toTeam: You are now talking to your team! + general: '&e[%1%&e] &f%2%' + noplayer: No player in the PVP arena. + notice: + awarded: You have been awarded %1% + nodropitem: Not so fast! No cheating! + noteleport: Please use '/pa leave' to exit the fight! + notice: 'Notice: %1%' + playerawarded: '%1% has been awarded %2%' + remove: '&cThis will permanently remove the arena &a%1%&c. Are you sure? Then + commit the command again!&f To disable this message, see ''safeadmin'' in your + config.yml' + waitingequal: Waiting for the teams to have an equal player number! + waitingforarena: Waiting for a running arena to finish! + welcomespec: Welcome to the spectator's area! + gameloot: Here is your game loot! + welcomespec2: /pa bet [name] [amount] to bet on team or player + youdeath: You entered a DEATH region. Goodbye! + youescaped: You escaped the battlefield. Goodbye! + youleft: You left the arena! + younocamp: You are in a NOCAMP region. Move! + playerhaswon: '%1% is the Champion!' + playerready: '%1%&e is ready!' + player: + prevented: + break: '&cYou may not break blocks!' + place: '&cYou may not place blocks!' + tnt: '&cYou may not use TNT!' + tntbreak: '&cYou may not break TNT!' + drop: '&cYou may not drop items!' + inventory: '&cYou may not access this!' + craft: '&cYou may not craft!' + notreadyplayers: Players not ready + players: Players + ready: + list: 'Players: %1%' + done: You have been flagged as ready! + region: + clear: + added: 'Added to region entity clearing whitelist: &a%1%&r' + list: 'Region entity clearing whitelist: &a%1%&r' + removed: 'Removed from region entity clearing whitelist: &a%1%&r' + flag: + added: 'Region flag added: &a%1%&r' + removed: 'Region flag removed: &a%1%&r' + height: 'Region height set to: &a%1%&r' + pos1: First position set. + pos2: Second position set. + protection_added: 'RegionProtection added: &a%1%&r' + protection_removed: 'RegionProtection removed: &a%1%&r' + radius: 'Region radius set to: &a%1%&r' + removed: 'Region removed: %1%' + saved: Region saved. + saved_notice: '&6You created a &oCUSTOM&6 region. It has no function yet! To turn + it into a battlefield region, type &r/pvparena %1% !rt %2% BATTLE.' + select: Select two points with your wand item, left click first and then right + click! + setting: Setting region enabled. + typeset: 'Region Type set: &e%1%' + youselect: You are now selecting a region for arena &a%1%&r! + regions: + flags: 'Region Flags: &a%1%&r' + head: '--- &aArena Region&r [&e%1%&r]---' + listhead: '--- &aArena Regions&r [&e%1%&r]---' + listvalue: '&a%1%&r: %2%, %3%' + protections: 'Region Protections: &a%1%&r' + shape: 'Region Shape: &a%1%&r' + type: 'Region Type: &a%1%&r' + reloaded: Config reloaded! + round: + roundsdisplay: Round %1% / %2% + roundsdisplayseparator: '-----------' + display: 'Round #%1%: %2%' + added: 'Added goal to round: &e%1%' + removed: 'Removed goal from round: &e%1%' + set: + items_not: Please use either hand or inventory to set an item node! + done: '&a%1%&r set to &e%2%&r!' + help: use /pa {arenaname} set [page] to get a node list + unknown: 'Unknown node: &e%1%&r!' + setowner: + done: '&a%1%&r is now owner of arena &a%2%&r!' + spawn: + freelounge: Lounge set + teamlounge: 'Lounge set: %1%' + notset: 'Spawn not set: &a%1%&r' + offset: Spawn &a%1%&r offset by &a%2%&r blocks. + removed: 'Spawn removed: &a%1%&r' + set: 'Spawn set: &a%1%&r' + setdone: 'Spawn setting done: &a%1%&r' + setstart: 'Spawn setting started: &a%1%&r' + unknown: 'Spawn not found: &a%1%&r' + stats: + filedone: Statistics file loaded! + head: Statistics TOP %1% (%2%) + typenotfound: 'Statistics type not found! Valid values: &e%1%&r' + stattype: + DAMAGE: full damage dealt + DAMAGETAKE: full damage taken + DEATHS: deaths + KILLS: kills + LOSSES: matches lost + MAXDAMAGE: max damage dealt + MAXDAMAGETAKE: max damage taken + 'NULL': player name + WINS: matches won + team: + haswon: Team %1% are the Champions! + ready: Team %1% is ready! + teams: + list: 'Available teams: %1%' + add: 'Team added: %1%' + set: 'Team set: %1%' + remove: 'Team removed: %1%' + template: + loaddone: 'Template loaded from: &a%1%&r' + savedone: 'Template saved to: &a%1%&r' + time: + minutes: minutes + seconds: seconds + timer: + countdowninterrupt: Countdown interrupted! Hit the ready block! + ending: The match will end in %1%! + resetting: The arena will reset in %1%! + starting: Starting in %1%! + warmingup: Warming up... %1%! + pvpactivating: PVP will be activated in %1%! + walls: Walls will be removed in %1%! + togglemod: + notice: '&cYou activated a module that requires a BATTLE region! Type: &r/pvparena + [arena] !rt [region] BATTLE' + uninstall: + done: 'uninstalled: &a%1%&r' + whitelist: + added: Added &a%1%&r to &e%2%&r whitelist! + allcleared: All whitelist cleared! + cleared: Whitelist &e%1%&r cleared! + help: 'Usage: blacklist clear | blacklist [type] [clear|add|remove] [id]' + removed: Removed &a%1%&r from &e%2%&r whitelist! + show: 'Whitelist &e%1%&r:' + mod: + walls: + fallingin: 'Walls fall in: %1%' + separator: '--------------------' + aftermatch: + aftermatch: The aftermatch has begun! + startingin: AfterMatch in %1%! + spawnnotset: Spawn 'after' not set! + announcements: + ignoreon: You are now ignoring announcements! + ignoreoff: You are now receiving announcements! + arenaboards: + createarenaboard: create an ArenaBoard + arenaboarddestroyed: ArenaBoard destroyed! + boardexists: ArenaBoard already exists!' + sortingby: ArenaBoard now sorted by %1% + autovote: + arenarunning: 'Arena running: %1%' + autojoin: Arena auto join started! + playervoted: '%2% voted for arena %1%!' + youvoted: You voted for arena %1%! + duel: + announcemoney: '&eThey set up a fee of &c%1%&e!' + cancelled: '&cThe duel has been cancelled!' + accepted: '%1% &eaccepted the challenge! The game is starting.' + announce: '%1% &echallenged you! Accept the duel with &f/pa %2% accept.' + announce2: '&eCancel the duel with &r/pa %2% decline&e.' + busy: '%1% &eis already in a fight Please try again later.' + declineds: Your opponent declined the request. The duel has been cancelled. + declinedr: You declined the duel request! + requested: You &echallenged &r%1%&e! + requestedalready: You already have challenged someone! + requestexpireds: Your opponent did not accept the request in time. The duel + has been cancelled. + requestexpiredr: You did not accept the request in time. The duel has been cancelled. + starting: The duel begins! + nodirectjoin: 'You may not join this arena directly! Use: &e/pa %1% duel [playername]' + fixinventorylos: + gamemode: Enter survival gamemode before joining! + invenory: Empty your inventory before joining! + latelounge: + llannounce: Arena %1% is starting! Player %2% wants to start. Join with /pa + %1% + llposition: You are on queue position %1%! + llrejoin: Ready check has caught you not being able to join. Rejoin when you + can! + llwait: Arena will be starting soon, please wait! + llleave: You have left the queue of the %1% arena. + playerfinder: + near: 'Nearest player: %1% blocks!' + point: Compass pointing to nearest player! + powerups: + invalidpowerupeffect: 'Invalid PowerupEffect: %1%' + puplayer: '%1% has collected PowerUp %2%!' + puserver: PowerUp deployed! + respawnrelay: + respawning: Respawning in %1%! + skins: + ld: Hooking into LibsDisguises! + dc: Hooking into DisguiseCraft! + md: Hooking into MobDisguise! + nomod: No disguise plugin found, Skins module is inactive! + showclass: Class &e%1%&r will be disguised to &a%2% + showteam: Team %1% will be disguised to %2% + specialjoin: + done: Join block set here - %1% + start: Setting join block! + stop: Aborted join block selection! + squads: + nosquad: 'No squads loaded! Add some: /pa [arena] !sq add [name]' + listhead: Squads for arena &b%1% + listitem: 'Squad %1% (max: %2%) %3%' + added: Squad %1% has been added + set: Squad %1% has been set + removed: Squad %1% has been removed + notexist: Squad %1% doesn't exist + error: Error while editing squads, syntax is not correct! + full: This squad is full! + help: |- + /pa !sq | show the arena squads + /pa !sq add [name] [limit] | add squad with player limit (set to 0 for no limit) + /pa !sq set [name] [limit] | set player limit for squad + /pa !sq remove [name] | remove squad [name] + startfreeze: + announce: The game will start in %1% seconds! + tempperms: + noperms: Permissions plugin not found, defaulting to OP. + head: 'Temporary permissions of &e%1%&r:' + added: Temporary permissions &e%1%&r added to &a%2%&r + removed: Temporary permissions &e%1%&r removed from &a%2%&r + vault: + theynotenough: '%1% doesn''t have enough cash!' + 'on': <3 eConomy + 'off': net.slipcor pvparena - 1.15.2-SNAPSHOT + 1.15.4-SNAPSHOT PVP Arena https://github.com/Eredrim/pvparena diff --git a/src/net/slipcor/pvparena/PVPArena.java b/src/net/slipcor/pvparena/PVPArena.java index 54f419bbb..f9ec66d76 100644 --- a/src/net/slipcor/pvparena/PVPArena.java +++ b/src/net/slipcor/pvparena/PVPArena.java @@ -102,65 +102,6 @@ public UpdateChecker getUpdateChecker() { return updateChecker; } - /** - * Check if a CommandSender has admin permissions - * - * @param sender the CommandSender to check - * @return true if a CommandSender has admin permissions, false otherwise - */ - public static boolean hasAdminPerms(final CommandSender sender) { - return sender.hasPermission("pvparena.admin"); - } - - /** - * Check if a CommandSender has creation permissions - * - * @param sender the CommandSender to check - * @param arena the arena to check - * @return true if the CommandSender has creation permissions, false - * otherwise - */ - public static boolean hasCreatePerms(final CommandSender sender, - final Arena arena) { - return sender.hasPermission("pvparena.create") && (arena == null || arena - .getOwner().equals(sender.getName())); - } - - public static boolean hasOverridePerms(final CommandSender sender) { - if (sender instanceof Player) { - return sender.hasPermission("pvparena.override"); - } - - return instance.getConfig().getBoolean("consoleoffduty") - != sender.hasPermission("pvparena.override"); - } - - /** - * Check if a CommandSender has permission for an arena - * - * @param sender the CommandSender to check - * @param arena the arena to check - * @return true if explicit permission not needed or granted, false - * otherwise - */ - public static boolean hasPerms(final CommandSender sender, final Arena arena) { - arena.getDebugger().i("perm check.", sender); - if (arena.getArenaConfig().getBoolean(CFG.PERMS_EXPLICITARENA)) { - arena.getDebugger().i( - " - explicit: " - + sender.hasPermission("pvparena.join." - + arena.getName().toLowerCase()), sender); - } else { - arena.getDebugger().i( - String.valueOf(sender.hasPermission("pvparena.user")), - sender); - } - - return arena.getArenaConfig().getBoolean(CFG.PERMS_EXPLICITARENA) ? sender - .hasPermission("pvparena.join." + arena.getName().toLowerCase()) - : sender.hasPermission("pvparena.user"); - } - public boolean isShuttingDown() { return shuttingDown; } diff --git a/src/net/slipcor/pvparena/api/IArenaCommandHandler.java b/src/net/slipcor/pvparena/api/IArenaCommandHandler.java index 4aa2ebeb1..7bfa5e9a9 100644 --- a/src/net/slipcor/pvparena/api/IArenaCommandHandler.java +++ b/src/net/slipcor/pvparena/api/IArenaCommandHandler.java @@ -30,11 +30,12 @@ public interface IArenaCommandHandler { CommandTree getSubs(final Arena arena); /** - * Does the sender have the permissions for this command? + * Does the sender have one of permissions for this command? * * @param sender the CommandSender to check * @param arena the arena they are part of + * @param silent display messages or not * @return true if they have the perms, false otherwise */ - boolean hasPerms(final CommandSender sender, final Arena arena); + boolean hasPerms(final CommandSender sender, final Arena arena, final boolean silent); } diff --git a/src/net/slipcor/pvparena/arena/Arena.java b/src/net/slipcor/pvparena/arena/Arena.java index 7aa3a1ea5..39f9fff49 100644 --- a/src/net/slipcor/pvparena/arena/Arena.java +++ b/src/net/slipcor/pvparena/arena/Arena.java @@ -2162,8 +2162,8 @@ && getClass(autoClass) == null) { final Arena arena = aPlayer.getArena(); - aPlayer.createState(player); ArenaPlayer.backupAndClearInventory(arena, player); + aPlayer.createState(player); aPlayer.dump(); diff --git a/src/net/slipcor/pvparena/arena/ArenaClass.java b/src/net/slipcor/pvparena/arena/ArenaClass.java index 0a78c7397..dba9bd75e 100644 --- a/src/net/slipcor/pvparena/arena/ArenaClass.java +++ b/src/net/slipcor/pvparena/arena/ArenaClass.java @@ -7,6 +7,7 @@ import org.bukkit.Material; import org.bukkit.block.Chest; import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -20,6 +21,7 @@ import static java.util.Arrays.asList; import static net.slipcor.pvparena.core.ItemStackUtils.getItemStacksFromConfig; +import static net.slipcor.pvparena.managers.ConfigurationManager.generateDefaultClasses; /** *
Arena Class class
@@ -41,7 +43,7 @@ public final class ArenaClass { private static final Map globals = new HashMap<>(); - private static final List OTHER_HELMET_LIST = asList(Material.PUMPKIN, Material.JACK_O_LANTERN, Material.PLAYER_HEAD); + private static final List OTHER_HELMET_LIST = asList(Material.PUMPKIN, Material.JACK_O_LANTERN); public static void addGlobalClasses() { @@ -49,53 +51,59 @@ public static void addGlobalClasses() { final File classFile = new File(PVPArena.instance.getDataFolder(), "classes.yml"); final YamlConfiguration cfg = YamlConfiguration.loadConfiguration(classFile); - cfg.addDefault("classes.Ranger", - "BOW,ARROW:64,LEATHER_HELMET,LEATHER_CHESTPLATE,LEATHER_LEGGINGS,LEATHER_BOOTS"); - cfg.addDefault("classes.Swordsman", - "DIAMOND_SWORD,IRON_HELMET,IRON_CHESTPLATE,IRON_LEGGINGS,IRON_BOOTS"); - cfg.addDefault("classes.Tank", - "STONE_SWORD,DIAMOND_HELMET,DIAMOND_CHESTPLATE,DIAMOND_LEGGINGS,DIAMOND_BOOTS"); - cfg.addDefault("classes.Pyro", - "FLINT_AND_STEEL,TNT:3,LEATHER_HELMET,LEATHER_CHESTPLATE,LEATHER_LEGGINGS,LEATHER_BOOTS"); + if(cfg.get("classes") == null) { + cfg.addDefault("classes", generateDefaultClasses()); + cfg.options().copyDefaults(true); - cfg.options().copyDefaults(); - try { - cfg.save(classFile); - } catch (final IOException e1) { - e1.printStackTrace(); + try { + cfg.save(classFile); + cfg.load(classFile); + } catch (IOException|InvalidConfigurationException e1) { + e1.printStackTrace(); + } } - for (final String className : cfg.getConfigurationSection("classes").getKeys(false)) { - ItemStack[] items; - ItemStack offHand; - ItemStack[] armors; + ConfigurationSection classesSection = cfg.getConfigurationSection("classes"); + if(classesSection != null) { + for (final String className : classesSection.getKeys(false)) { + ItemStack[] items; + ItemStack offHand; + ItemStack[] armors; + + try { + ConfigurationSection classesCfg = classesSection.getConfigurationSection(className); + items = getItemStacksFromConfig(classesCfg.getList("items")); + offHand = getItemStacksFromConfig(classesCfg.getList("offhand"))[0]; + armors = getItemStacksFromConfig(classesCfg.getList("armor")); + } catch (final Exception e) { + PVPArena.instance.getLogger().severe( + "(classes.yml) Error while parsing class, skipping: " + className); + e.printStackTrace(); + continue; + } + + final String classChest; + if (cfg.contains("classchests." + className)) { + classChest = (String) cfg.getConfigurationSection("classchests").get(className); + try { + PABlockLocation loc = new PABlockLocation(classChest); + Chest c = (Chest) loc.toLocation().getBlock().getState(); + ItemStack[] contents = c.getInventory().getContents(); + items = Arrays.copyOfRange(contents, 0, contents.length-5); + offHand = contents[contents.length-5]; + armors = Arrays.copyOfRange(contents, contents.length-4, contents.length); + } catch (Exception e) { + PVPArena.instance.getLogger().severe( + "(classes.yml) Error while parsing location of classchest, skipping: " + className); + e.printStackTrace(); + continue; + } + } - try { - ConfigurationSection classesCfg = cfg.getConfigurationSection("classes").getConfigurationSection(className); - items = getItemStacksFromConfig(classesCfg.getList("items")); - offHand = getItemStacksFromConfig(classesCfg.getList("items"))[0]; - armors = getItemStacksFromConfig(classesCfg.getList("items")); - } catch (final Exception e) { - Bukkit.getLogger().severe( - "[PVP Arena] Error while parsing class, skipping: " - + className); - e.printStackTrace(); - continue; - } - final String classChest; - try { - classChest = (String) cfg.getConfigurationSection("classchests").get(className); - PABlockLocation loc = new PABlockLocation(classChest); - Chest c = (Chest) loc.toLocation().getBlock().getState(); - ItemStack[] contents = c.getInventory().getContents(); - items = Arrays.copyOfRange(contents, 0, contents.length-5); - offHand = contents[contents.length-5]; - armors = Arrays.copyOfRange(contents, contents.length-4, contents.length); - globals.put(className, new ArenaClass(className, items, offHand, armors)); - } catch (Exception e) { globals.put(className, new ArenaClass(className, items, offHand, armors)); } } + } public static void addGlobalClasses(final Arena arena) { @@ -234,7 +242,7 @@ public ItemStack[] getItems() { private static boolean isHelmetItem(Material material) { return material.name().endsWith("_HELMET") || material.name().endsWith("_WOOL") || - OTHER_HELMET_LIST.contains(material); + material.name().endsWith("_HEAD") || OTHER_HELMET_LIST.contains(material); } private static boolean isChestplateItem(Material material) { diff --git a/src/net/slipcor/pvparena/arena/PlayerState.java b/src/net/slipcor/pvparena/arena/PlayerState.java index f9e92494e..4ee997ef3 100644 --- a/src/net/slipcor/pvparena/arena/PlayerState.java +++ b/src/net/slipcor/pvparena/arena/PlayerState.java @@ -37,6 +37,8 @@ public final class PlayerState { private double health; private double maxhealth; private int explevel; + private float walkSpeed; + private float flySpeed; private float exhaustion; private float experience; @@ -48,22 +50,25 @@ public final class PlayerState { private Collection potionEffects; public PlayerState(final Player player) { - name = player.getName(); - debug.i("creating PlayerState of " + name, player); + this.name = player.getName(); + debug.i("creating PlayerState of " + this.name, player); - fireticks = player.getFireTicks(); - foodlevel = player.getFoodLevel(); - gamemode = player.getGameMode().getValue(); - health = player.getHealth(); - maxhealth = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue(); + this.fireticks = player.getFireTicks(); + this.foodlevel = player.getFoodLevel(); + this.gamemode = player.getGameMode().getValue(); + this.health = player.getHealth(); + this.maxhealth = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue(); - exhaustion = player.getExhaustion(); - experience = player.getExp(); - explevel = player.getLevel(); - saturation = player.getSaturation(); + this.exhaustion = player.getExhaustion(); + this.experience = player.getExp(); + this.explevel = player.getLevel(); + this.saturation = player.getSaturation(); - potionEffects = player.getActivePotionEffects(); - collides = player.isCollidable(); + this.walkSpeed = player.getWalkSpeed(); + this.flySpeed = player.getFlySpeed(); + + this.potionEffects = player.getActivePotionEffects(); + this.collides = player.isCollidable(); final ArenaPlayer aPlayer = ArenaPlayer.parsePlayer(player.getName()); final Arena arena = aPlayer.getArena(); @@ -71,7 +76,7 @@ public PlayerState(final Player player) { aPlayer.setFlyState(player.isFlying()); if (arena.getArenaConfig().getBoolean(CFG.CHAT_COLORNICK)) { - displayname = player.getDisplayName(); + this.displayname = player.getDisplayName(); } fullReset(arena, player); @@ -82,19 +87,21 @@ public PlayerState(final Player player) { } public void dump(final YamlConfiguration cfg) { - debug.i("backing up PlayerState of " + name, name); - cfg.set("state.fireticks", fireticks); - cfg.set("state.foodlevel", foodlevel); - cfg.set("state.gamemode", gamemode); - cfg.set("state.health", health); - cfg.set("state.maxhealth", maxhealth); - cfg.set("state.exhaustion", exhaustion); - cfg.set("state.experience", experience); - cfg.set("state.explevel", explevel); - cfg.set("state.saturation", saturation); - cfg.set("state.displayname", displayname); - cfg.set("state.flying", ArenaPlayer.parsePlayer(name).getFlyState()); - cfg.set("state.collides", collides); + debug.i("backing up PlayerState of " + this.name, this.name); + cfg.set("state.fireticks", this.fireticks); + cfg.set("state.foodlevel", this.foodlevel); + cfg.set("state.gamemode", this.gamemode); + cfg.set("state.health", this.health); + cfg.set("state.maxhealth", this.maxhealth); + cfg.set("state.exhaustion", this.exhaustion); + cfg.set("state.experience", this.experience); + cfg.set("state.explevel", this.explevel); + cfg.set("state.saturation", this.saturation); + cfg.set("state.displayname", this.displayname); + cfg.set("state.flying", ArenaPlayer.parsePlayer(this.name).getFlyState()); + cfg.set("state.walkSpeed", this.walkSpeed); + cfg.set("state.flySpeed", this.flySpeed); + cfg.set("state.collides", this.collides); } public static void fullReset(final Arena arena, final Player player) { @@ -147,35 +154,37 @@ public static void fullReset(final Arena arena, final Player player) { player.setDisplayName(n); } + player.setWalkSpeed(0.2F); + player.setFlySpeed(0.2F); } public void unload(final boolean soft) { - final Player player = Bukkit.getPlayerExact(name); + final Player player = Bukkit.getPlayerExact(this.name); if (player == null) { - final ArenaPlayer aPlayer = ArenaPlayer.parsePlayer(name); + final ArenaPlayer aPlayer = ArenaPlayer.parsePlayer(this.name); PVPArena.instance.getAgm().disconnect(aPlayer.getArena(), aPlayer); return; } - debug.i("restoring PlayerState of " + name, player); + debug.i("restoring PlayerState of " + this.name, player); - player.setFireTicks(fireticks); - player.setFoodLevel(foodlevel); + player.setFireTicks(this.fireticks); + player.setFoodLevel(this.foodlevel); final ArenaPlayer aPlayer = ArenaPlayer.parsePlayer(player.getName()); - player.setFoodLevel(foodlevel); + player.setFoodLevel(this.foodlevel); if (aPlayer.getArena().getArenaConfig().getInt(CFG.GENERAL_GAMEMODE) > -1) { - player.setGameMode(GameMode.getByValue(gamemode)); + player.setGameMode(GameMode.getByValue(this.gamemode)); } if (aPlayer.getArena().getArenaConfig().getInt(CFG.PLAYER_MAXHEALTH) > 0) { - player.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(maxhealth); + player.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(this.maxhealth); } - if (player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue() == maxhealth) { - player.setHealth(Math.min(health, maxhealth)); + if (player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue() == this.maxhealth) { + player.setHealth(Math.min(this.health, this.maxhealth)); } else { - final double newHealth = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue() * health / maxhealth; + final double newHealth = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue() * this.health / this.maxhealth; if (newHealth > player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue()) { player.setHealth(player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue()); } else { @@ -183,17 +192,17 @@ public void unload(final boolean soft) { } } - player.setSaturation(saturation); + player.setSaturation(this.saturation); if (aPlayer.getArena().getArenaConfig().getInt(CFG.GENERAL_GAMEMODE) > -1) { - player.setGameMode(GameMode.getByValue(gamemode)); + player.setGameMode(GameMode.getByValue(this.gamemode)); } - player.setLevel(explevel); - player.setExp(experience); - player.setExhaustion(exhaustion); + player.setLevel(this.explevel); + player.setExp(this.experience); + player.setExhaustion(this.exhaustion); player.setFallDistance(0); player.setVelocity(new Vector()); if (aPlayer.getArena() != null && aPlayer.getArena().getArenaConfig().getBoolean(CFG.CHAT_COLORNICK)) { - player.setDisplayName(displayname); + player.setDisplayName(this.displayname); } if (aPlayer.getArena() != null) { @@ -203,7 +212,7 @@ public void unload(final boolean soft) { removeEffects(player); - player.addPotionEffects(potionEffects); + player.addPotionEffects(this.potionEffects); aPlayer.setTelePass(false); player.setFireTicks(0); @@ -223,13 +232,15 @@ public void run() { player.setNoDamageTicks(aPlayer.getArena().getArenaConfig().getInt(CFG.TIME_TELEPORTPROTECT) * 20); } player.resetPlayerTime(); - player.setCollidable(collides); + player.setCollidable(this.collides); if (!soft) { if (aPlayer.getFlyState() && !player.getAllowFlight()) { player.setAllowFlight(true); } player.setFlying(aPlayer.getFlyState()); } + player.setFlySpeed(this.flySpeed); + player.setWalkSpeed(this.walkSpeed); } /** @@ -251,20 +262,23 @@ public static void playersetHealth(final Player player, final double value) { } public void reset() { - debug.i("clearing PlayerState of " + name, name); - fireticks = 0; - foodlevel = 0; - gamemode = 0; - health = 0; - maxhealth = -1; - - exhaustion = 0; - experience = 0; - explevel = 0; - saturation = 0; - displayname = null; - potionEffects = null; - collides = false; + debug.i("clearing PlayerState of " + this.name, this.name); + this.fireticks = 0; + this.foodlevel = 0; + this.gamemode = 0; + this.health = 0; + this.maxhealth = -1; + + this.exhaustion = 0; + this.experience = 0; + this.explevel = 0; + this.saturation = 0; + this.displayname = null; + this.potionEffects = null; + this.collides = false; + this.walkSpeed = 0.2f; + this.flySpeed = 0.2f; + } public static void removeEffects(final Player player) { @@ -303,7 +317,9 @@ public static PlayerState undump(final YamlConfiguration cfg, final String pName pState.displayname = cfg.getString("state.displayname", pName); ArenaPlayer.parsePlayer(pName).setFlyState(cfg.getBoolean("state.flying", false)); pState.collides = cfg.getBoolean("state.collides", false); + pState.walkSpeed = (float) cfg.getDouble("state.walkSpeed", 0.2f); + pState.flySpeed = (float) cfg.getDouble("state.flySpeed", 0.2f); return pState; } -} \ No newline at end of file +} diff --git a/src/net/slipcor/pvparena/commands/AbstractArenaCommand.java b/src/net/slipcor/pvparena/commands/AbstractArenaCommand.java index ad7440993..42e72d9b3 100644 --- a/src/net/slipcor/pvparena/commands/AbstractArenaCommand.java +++ b/src/net/slipcor/pvparena/commands/AbstractArenaCommand.java @@ -5,8 +5,11 @@ import net.slipcor.pvparena.core.Language; import net.slipcor.pvparena.core.Language.MSG; import net.slipcor.pvparena.core.StringParser; +import net.slipcor.pvparena.managers.PermissionManager; import org.bukkit.command.CommandSender; +import java.util.Arrays; + /** *
  * PVP Arena ArenaCommand class
@@ -19,19 +22,17 @@
  */
 
 public abstract class AbstractArenaCommand implements IArenaCommandHandler {
-    private final String[] perms;
+    protected final String[] perms;
 
     AbstractArenaCommand(final String[] permissions) {
-        perms = permissions.clone();
+        this.perms = permissions.clone();
     }
 
     public static boolean argCountValid(final CommandSender sender, final Arena arena,
                                         final String[] args, final Integer[] validCounts) {
 
-        for (final int i : validCounts) {
-            if (i == args.length) {
-                return true;
-            }
+        if (Arrays.stream(validCounts).anyMatch(count -> count == args.length)) {
+            return true;
         }
 
         final String msg = Language.parse(arena, MSG.ERROR_INVALID_ARGUMENT_COUNT,
@@ -50,24 +51,24 @@ public static boolean argCountValid(final CommandSender sender, final Arena aren
 
     public abstract String getName();
 
+    public boolean hasPerms(CommandSender sender, Arena arena) {
+        return hasPerms(sender, arena, false);
+    }
+
     @Override
-    public boolean hasPerms(final CommandSender sender, final Arena arena) {
-        if (sender.hasPermission("pvparena.admin")) {
-            return true;
-        }
+    public boolean hasPerms(CommandSender sender, Arena arena, boolean silent) {
 
-        if (arena != null && sender.hasPermission("pvparena.create")
-                && sender.getName().equals(arena.getOwner())) {
+        if (arena != null && PermissionManager.hasBuilderPerm(sender, arena)) {
             return true;
         }
 
-        for (final String perm : perms) {
-            if (sender.hasPermission(perm)) {
-                return true;
-            }
+        boolean hasPermission = Arrays.stream(this.perms).anyMatch(sender::hasPermission);
+        if (!silent && !hasPermission) {
+            Arrays.stream(this.perms)
+                    .forEach(perm -> Arena.pmsg(sender, PermissionManager.getMissingPermissionMessage(perm)));
         }
 
-        return false;
+        return hasPermission;
     }
 
     public abstract void displayHelp(final CommandSender sender);
diff --git a/src/net/slipcor/pvparena/commands/AbstractGlobalCommand.java b/src/net/slipcor/pvparena/commands/AbstractGlobalCommand.java
index 259359d43..cda7013db 100644
--- a/src/net/slipcor/pvparena/commands/AbstractGlobalCommand.java
+++ b/src/net/slipcor/pvparena/commands/AbstractGlobalCommand.java
@@ -1,13 +1,17 @@
 package net.slipcor.pvparena.commands;
 
-import net.slipcor.pvparena.PVPArena;
 import net.slipcor.pvparena.api.IArenaCommandHandler;
 import net.slipcor.pvparena.arena.Arena;
 import net.slipcor.pvparena.core.Language;
 import net.slipcor.pvparena.core.Language.MSG;
 import net.slipcor.pvparena.core.StringParser;
+import net.slipcor.pvparena.managers.PermissionManager;
 import org.bukkit.command.CommandSender;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * 
  * PVP Arena Global Command class
@@ -23,16 +27,14 @@ public abstract class AbstractGlobalCommand implements IArenaCommandHandler {
     private final String[] perms;
 
     AbstractGlobalCommand(final String[] permissions) {
-        perms = permissions.clone();
+        this.perms = permissions.clone();
     }
 
     static boolean argCountValid(final CommandSender sender, final String[] args,
                                  final Integer[] validCounts) {
 
-        for (final int i : validCounts) {
-            if (i == args.length) {
-                return true;
-            }
+        if (Arrays.stream(validCounts).anyMatch(count -> count == args.length)) {
+            return true;
         }
 
         Arena.pmsg(
@@ -49,63 +51,49 @@ static boolean argCountValid(final CommandSender sender, final String[] args,
 
     /**
      * Check if the global command also exists in arena context
+     *
      * @return true if there is the same command for arena context
      */
     public boolean hasVersionForArena() {
         return false;
     }
 
+    public boolean hasPerms(final CommandSender sender) {
+        return hasPerms(sender, null, false);
+    }
+
     @Override
-    public boolean hasPerms(final CommandSender sender, final Arena arena) {
-        // tabComplete check
-        if (PVPArena.hasAdminPerms(sender)) {
+    public boolean hasPerms(final CommandSender sender, final Arena arena, final boolean silent) {
+
+        if (PermissionManager.hasAdminPerm(sender)) {
             return true;
         }
 
-        for (final String perm : perms) {
+        boolean hasNotPermission = false;
+        List messages = new ArrayList<>();
+
+        for (String perm : this.perms) {
             if (sender.hasPermission(perm)) {
                 return true;
             }
-        }
-        return false;
-    }
 
-    boolean hasPerms(final CommandSender sender) {
-        if (PVPArena.hasAdminPerms(sender)) {
-            return true;
+            // Get the permission deny message
+            if (!silent) {
+                messages.add(PermissionManager.getMissingPermissionMessage(perm));
+                hasNotPermission = true;
+            }
         }
 
-        boolean done = false;
-
-        for (final String perm : perms) {
-            if (sender.hasPermission(perm)) {
-                return true;
-            }
-            final String[] split = perm.split("\\.");
-            String permString = split[1];
-            try {
-                if (split.length > 2) {
-                    permString = split[1]+"."+split[2];
-                }
+        if (!silent) {
+            if (hasNotPermission) {
+                messages.forEach(message -> Arena.pmsg(sender, message));
+            } else {
+                // perms is empty
                 Arena.pmsg(
                         sender,
                         Language.parse(MSG.ERROR_NOPERM,
-                                Language.parse(MSG.getByNode("nulang.nopermto." + permString))));
-            } catch (final Exception e) {
-                PVPArena.instance.getLogger().warning("Unknown MSG for pvparena." + permString);
-                Arena.pmsg(
-                        sender,
-                        Language.parse(MSG.ERROR_NOPERM,
-                                Language.parse(MSG.ERROR_NOPERM_X_USER)));
+                                MSG.ERROR_NOPERM_X_ADMIN.toString()));
             }
-            done = true;
-        }
-
-        if (!done) {
-            Arena.pmsg(
-                    sender,
-                    Language.parse(MSG.ERROR_NOPERM,
-                            MSG.ERROR_NOPERM_X_ADMIN.toString()));
         }
 
         return false;
diff --git a/src/net/slipcor/pvparena/commands/PAA_Class.java b/src/net/slipcor/pvparena/commands/PAA_Class.java
index c6769c098..ba0a5b67c 100644
--- a/src/net/slipcor/pvparena/commands/PAA_Class.java
+++ b/src/net/slipcor/pvparena/commands/PAA_Class.java
@@ -1,6 +1,5 @@
 package net.slipcor.pvparena.commands;
 
-import net.slipcor.pvparena.PVPArena;
 import net.slipcor.pvparena.arena.Arena;
 import net.slipcor.pvparena.arena.ArenaClass;
 import net.slipcor.pvparena.arena.ArenaPlayer;
@@ -13,7 +12,6 @@
 import org.bukkit.entity.Player;
 import org.bukkit.inventory.ItemStack;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -55,43 +53,45 @@ public void commit(final Arena arena, final CommandSender sender, final String[]
         // /pa {arenaname} class load [name]
         // /pa {arenaname} class remove [name]
 
-        if (args.length == 1) {
-            final Player player = (Player) sender;
-            PVPArena.instance.getLogger().info("Exiting edit mode: " + player.getName());
-
-            final ArenaPlayer aPlayer = ArenaPlayer.parsePlayer(player.getName());
-
-            ArenaPlayer.reloadInventory(arena, player, false);
+        final Player player = (Player) sender;
+        final ArenaPlayer aPlayer = ArenaPlayer.parsePlayer(sender.getName());
+        String classname;
 
-            aPlayer.setArena(null);
-            return;
+        if (args.length == 1) {
+            // when no 2nd arg, save/remove/load class with name of player's current class
+            if (aPlayer.getArenaClass() == null) {
+                Arena.pmsg(player, Language.parse(arena, MSG.ERROR_CLASS_NOT_GIVEN));
+                return;
+            }
+            classname = aPlayer.getArenaClass().getName();
+        } else {
+            classname = args[1];
         }
 
-        if ("save".equalsIgnoreCase(args[0])) {
-            final Player player = (Player) sender;
-            final List items = new ArrayList<>();
-
-            arena.getArenaConfig().setManually("classitems." + args[1] + ".items", getSerializableItemStacks(player.getInventory().getStorageContents()));
-            arena.getArenaConfig().setManually("classitems." + args[1] + ".offhand", getSerializableItemStacks(player.getInventory().getItemInOffHand()));
-            arena.getArenaConfig().setManually("classitems." + args[1] + ".armor", getSerializableItemStacks(player.getInventory().getArmorContents()));
-            arena.getArenaConfig().save();
-
-            arena.addClass(args[1], player.getInventory().getStorageContents(), player.getInventory().getItemInOffHand(), player.getInventory().getArmorContents());
-            Arena.pmsg(player, Language.parse(arena, MSG.CLASS_SAVED, args[1]));
-        } else if ("load".equalsIgnoreCase(args[0])) {
-            final ArenaPlayer aPlayer = ArenaPlayer.parsePlayer(sender.getName());
+        if ("load".equalsIgnoreCase(args[0])) {
             if(aPlayer.getArenaClass() == null) {
-                ArenaPlayer.backupAndClearInventory(arena, aPlayer.get());
+                ArenaPlayer.backupAndClearInventory(arena, player);
             } else {
-                InventoryManager.clearInventory(aPlayer.get());
+                InventoryManager.clearInventory(player);
             }
-            arena.selectClass(aPlayer, args[1]);
+            arena.selectClass(aPlayer, classname);
+        } else if ("save".equalsIgnoreCase(args[0])) {
+            ItemStack[] storage = player.getInventory().getStorageContents();
+            ItemStack offhand = player.getInventory().getItemInOffHand();
+            ItemStack[] armor = player.getInventory().getArmorContents();
+    
+            arena.getArenaConfig().setManually("classitems." + classname + ".items", getSerializableItemStacks(storage));
+            arena.getArenaConfig().setManually("classitems." + classname + ".offhand", getSerializableItemStacks(offhand));
+            arena.getArenaConfig().setManually("classitems." + classname + ".armor", getSerializableItemStacks(armor));
+            arena.getArenaConfig().save();
+    
+            arena.addClass(classname, storage, offhand, armor);
+            Arena.pmsg(player, Language.parse(arena, MSG.CLASS_SAVED, classname));
         } else if ("remove".equalsIgnoreCase(args[0])) {
-            final Player player = (Player) sender;
-            arena.getArenaConfig().setManually("classitems." + args[1], null);
+            arena.getArenaConfig().setManually("classitems." + classname, null);
             arena.getArenaConfig().save();
-            arena.removeClass(args[1]);
-            Arena.pmsg(player, Language.parse(arena, MSG.CLASS_REMOVED, args[1]));
+            arena.removeClass(classname);
+            Arena.pmsg(player, Language.parse(arena, MSG.CLASS_REMOVED, classname));
         }
     }
 
diff --git a/src/net/slipcor/pvparena/commands/PAA_Create.java b/src/net/slipcor/pvparena/commands/PAA_Create.java
index c50239d34..ca5ba718b 100644
--- a/src/net/slipcor/pvparena/commands/PAA_Create.java
+++ b/src/net/slipcor/pvparena/commands/PAA_Create.java
@@ -6,6 +6,7 @@
 import net.slipcor.pvparena.core.Language;
 import net.slipcor.pvparena.core.Language.MSG;
 import net.slipcor.pvparena.managers.ArenaManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 
@@ -54,7 +55,7 @@ public void commit(final CommandSender sender, final String[] args) {
 
         arena = new Arena(args[0]);
 
-        if (!sender.hasPermission("pvparena.admin")) {
+        if (!PermissionManager.hasAdminPerm(sender)) {
             // no admin perms => create perms => set owner
             arena.setOwner(sender.getName());
         }
diff --git a/src/net/slipcor/pvparena/commands/PAA_PlayerClass.java b/src/net/slipcor/pvparena/commands/PAA_PlayerClass.java
index 13439c8f3..a59537941 100644
--- a/src/net/slipcor/pvparena/commands/PAA_PlayerClass.java
+++ b/src/net/slipcor/pvparena/commands/PAA_PlayerClass.java
@@ -7,6 +7,7 @@
 import net.slipcor.pvparena.core.Help.HELP;
 import net.slipcor.pvparena.core.Language;
 import net.slipcor.pvparena.core.Language.MSG;
+import net.slipcor.pvparena.managers.PermissionManager;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 
@@ -49,7 +50,7 @@ public void commit(final Arena arena, final CommandSender sender, final String[]
         final String className;
 
         if (args.length > 1) {
-            if (PVPArena.hasCreatePerms(sender, arena)) {
+            if (PermissionManager.hasBuilderPerm(sender, arena)) {
                 className = args[1];
             } else {
                 className = sender.getName();
diff --git a/src/net/slipcor/pvparena/commands/PAA_Reload.java b/src/net/slipcor/pvparena/commands/PAA_Reload.java
index c09a79587..70b4d161e 100644
--- a/src/net/slipcor/pvparena/commands/PAA_Reload.java
+++ b/src/net/slipcor/pvparena/commands/PAA_Reload.java
@@ -22,8 +22,12 @@
 
 public class PAA_Reload extends AbstractArenaCommand {
 
+    public static final String CMD_RELOAD_PERM = "pvparena.cmds.reload";
+    public static final String RELOAD = "reload";
+    public static final String RELOAD_SHORT = "!rl";
+
     public PAA_Reload() {
-        super(new String[]{"pvparena.cmds.reload"});
+        super(new String[]{CMD_RELOAD_PERM});
     }
 
     @Override
@@ -58,12 +62,12 @@ public void displayHelp(final CommandSender sender) {
 
     @Override
     public List getMain() {
-        return Collections.singletonList("reload");
+        return Collections.singletonList(RELOAD);
     }
 
     @Override
     public List getShort() {
-        return Collections.singletonList("!rl");
+        return Collections.singletonList(RELOAD_SHORT);
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAA_ReloadAll.java b/src/net/slipcor/pvparena/commands/PAA_ReloadAll.java
index 3e751d230..c8c192676 100644
--- a/src/net/slipcor/pvparena/commands/PAA_ReloadAll.java
+++ b/src/net/slipcor/pvparena/commands/PAA_ReloadAll.java
@@ -2,6 +2,7 @@
 
 import net.slipcor.pvparena.PVPArena;
 import net.slipcor.pvparena.arena.Arena;
+import net.slipcor.pvparena.arena.ArenaClass;
 import net.slipcor.pvparena.core.Help;
 import net.slipcor.pvparena.core.Help.HELP;
 import net.slipcor.pvparena.core.Language;
@@ -56,6 +57,7 @@ public void commit(final CommandSender sender, final String[] args) {
             scmd.commit(a, sender, emptyArray);
         }
 
+        ArenaClass.addGlobalClasses(); // reload classes.yml
         ArenaManager.load_arenas();
         if (config.getBoolean("use_shortcuts") || config.getBoolean("only_shortcuts")) {
             ArenaManager.readShortcuts(config.getConfigurationSection("shortcuts"));
diff --git a/src/net/slipcor/pvparena/commands/PAG_Arenaclass.java b/src/net/slipcor/pvparena/commands/PAG_Arenaclass.java
index 429ff74db..e55ab893d 100644
--- a/src/net/slipcor/pvparena/commands/PAG_Arenaclass.java
+++ b/src/net/slipcor/pvparena/commands/PAG_Arenaclass.java
@@ -12,6 +12,7 @@
 import net.slipcor.pvparena.core.StringParser;
 import net.slipcor.pvparena.loadables.ArenaModuleManager;
 import net.slipcor.pvparena.managers.InventoryManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import org.bukkit.ChatColor;
 import org.bukkit.block.Sign;
 import org.bukkit.command.CommandSender;
@@ -37,7 +38,7 @@
 
 public class PAG_Arenaclass extends AbstractArenaCommand {
     public PAG_Arenaclass() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.arenaclass"});
+        super(new String[]{"pvparena.cmds.arenaclass"});
     }
 
     @Override
@@ -84,8 +85,7 @@ public void commit(final Arena arena, final CommandSender sender, final String[]
             return;
         }
 
-        if (arena.getArenaConfig().getBoolean(CFG.PERMS_EXPLICITCLASS)
-                && !sender.hasPermission("pvparena.class." + aClass.getName())) {
+        if (!PermissionManager.hasExplicitClassPerm(sender, arena, aClass)) {
             arena.msg(sender,
                     Language.parse(arena, MSG.ERROR_NOPERM_CLASS, aClass.getName()));
             return;
diff --git a/src/net/slipcor/pvparena/commands/PAG_Chat.java b/src/net/slipcor/pvparena/commands/PAG_Chat.java
index d4ee58ed5..d3a7742e0 100644
--- a/src/net/slipcor/pvparena/commands/PAG_Chat.java
+++ b/src/net/slipcor/pvparena/commands/PAG_Chat.java
@@ -26,7 +26,7 @@
 public class PAG_Chat extends AbstractArenaCommand {
 
     public PAG_Chat() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.chat"});
+        super(new String[]{"pvparena.cmds.chat"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAG_Join.java b/src/net/slipcor/pvparena/commands/PAG_Join.java
index 94f1c61dc..58150e8eb 100644
--- a/src/net/slipcor/pvparena/commands/PAG_Join.java
+++ b/src/net/slipcor/pvparena/commands/PAG_Join.java
@@ -1,6 +1,5 @@
 package net.slipcor.pvparena.commands;
 
-import net.slipcor.pvparena.PVPArena;
 import net.slipcor.pvparena.arena.Arena;
 import net.slipcor.pvparena.arena.ArenaPlayer;
 import net.slipcor.pvparena.classes.PACheck;
@@ -12,6 +11,7 @@
 import net.slipcor.pvparena.loadables.ArenaRegion;
 import net.slipcor.pvparena.managers.ArenaManager;
 import net.slipcor.pvparena.managers.ConfigurationManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 
@@ -29,15 +29,20 @@
 
 public class PAG_Join extends AbstractArenaCommand {
 
+    public static final String JOIN = "join";
+    public static final String JOIN_SHORT = "-j";
+
     //private final Debug debug = new Debug(200);
 
     public PAG_Join() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.join"});
+        super(new String[]{"pvparena.cmds.join"});
     }
 
     @Override
     public void commit(final Arena arena, final CommandSender sender, final String[] args) {
-        if (!hasPerms(sender, arena)) {
+        if (!(hasPerms(sender, arena) && PermissionManager.hasExplicitArenaPerm(sender, arena, JOIN))) {
+            arena.getDebugger().i(String.join(", ", this.perms));
+            arena.msg(sender, Language.parse(arena, MSG.ERROR_NOPERM_JOIN));
             return;
         }
 
@@ -61,11 +66,6 @@ public void commit(final Arena arena, final CommandSender sender, final String[]
             return;
         }
 
-        if (!PVPArena.hasPerms(sender, arena)) {
-            arena.msg(sender, Language.parse(arena, MSG.ERROR_NOPERM_JOIN));
-            return;
-        }
-
         final String error = ConfigurationManager.isSetup(arena);
         if (error != null) {
             arena.msg(sender, Language.parse(arena, MSG.ERROR_ERROR, error));
@@ -108,12 +108,12 @@ public void displayHelp(final CommandSender sender) {
 
     @Override
     public List getMain() {
-        return Collections.singletonList("join");
+        return Collections.singletonList(JOIN);
     }
 
     @Override
     public List getShort() {
-        return Collections.singletonList("-j");
+        return Collections.singletonList(JOIN_SHORT);
     }
 
     @Override
@@ -129,4 +129,5 @@ public CommandTree getSubs(final Arena arena) {
         }
         return result;
     }
+
 }
diff --git a/src/net/slipcor/pvparena/commands/PAG_Leave.java b/src/net/slipcor/pvparena/commands/PAG_Leave.java
index 501eb4ff7..a5aba17e3 100644
--- a/src/net/slipcor/pvparena/commands/PAG_Leave.java
+++ b/src/net/slipcor/pvparena/commands/PAG_Leave.java
@@ -26,7 +26,7 @@
 public class PAG_Leave extends AbstractArenaCommand {
 
     public PAG_Leave() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.leave"});
+        super(new String[]{"pvparena.cmds.leave"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAG_Spectate.java b/src/net/slipcor/pvparena/commands/PAG_Spectate.java
index a46415d67..48936b996 100644
--- a/src/net/slipcor/pvparena/commands/PAG_Spectate.java
+++ b/src/net/slipcor/pvparena/commands/PAG_Spectate.java
@@ -25,7 +25,7 @@
 public class PAG_Spectate extends AbstractArenaCommand {
 
     public PAG_Spectate() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.spectate"});
+        super(new String[]{"pvparena.cmds.spectate"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAI_ArenaList.java b/src/net/slipcor/pvparena/commands/PAI_ArenaList.java
index fcdd5b628..d085422e0 100644
--- a/src/net/slipcor/pvparena/commands/PAI_ArenaList.java
+++ b/src/net/slipcor/pvparena/commands/PAI_ArenaList.java
@@ -1,6 +1,5 @@
 package net.slipcor.pvparena.commands;
 
-import net.slipcor.pvparena.PVPArena;
 import net.slipcor.pvparena.arena.Arena;
 import net.slipcor.pvparena.core.Help;
 import net.slipcor.pvparena.core.Help.HELP;
@@ -8,6 +7,7 @@
 import net.slipcor.pvparena.core.Language.MSG;
 import net.slipcor.pvparena.core.StringParser;
 import net.slipcor.pvparena.managers.ArenaManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import org.bukkit.command.CommandSender;
 
 import java.util.ArrayList;
@@ -26,7 +26,7 @@
 public class PAI_ArenaList extends AbstractGlobalCommand {
 
     public PAI_ArenaList() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.arenalist"});
+        super(new String[]{"pvparena.cmds.arenalist"});
     }
 
     @Override
@@ -40,7 +40,7 @@ public void commit(final CommandSender sender, final String[] args) {
         }
         final List names;
 
-        if (!PVPArena.hasOverridePerms(sender) && ArenaManager.isUsingShortcuts()) {
+        if (!PermissionManager.hasOverridePerm(sender) && ArenaManager.isUsingShortcuts()) {
             names = ArenaManager.getColoredShortcuts();
         } else {
             names = new ArrayList<>();
diff --git a/src/net/slipcor/pvparena/commands/PAI_GlobalStats.java b/src/net/slipcor/pvparena/commands/PAI_GlobalStats.java
index f42826180..70dc80516 100644
--- a/src/net/slipcor/pvparena/commands/PAI_GlobalStats.java
+++ b/src/net/slipcor/pvparena/commands/PAI_GlobalStats.java
@@ -21,7 +21,7 @@
 public class PAI_GlobalStats extends AbstractGlobalCommand {
 
     public PAI_GlobalStats() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.stats"});
+        super(new String[]{"pvparena.cmds.stats"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAI_Help.java b/src/net/slipcor/pvparena/commands/PAI_Help.java
index 8e46a7cc8..aa50fd544 100644
--- a/src/net/slipcor/pvparena/commands/PAI_Help.java
+++ b/src/net/slipcor/pvparena/commands/PAI_Help.java
@@ -23,7 +23,7 @@
 public class PAI_Help extends AbstractGlobalCommand {
 
     public PAI_Help() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.help"});
+        super(new String[]{"pvparena.cmds.help"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAI_Info.java b/src/net/slipcor/pvparena/commands/PAI_Info.java
index 42ce9da7b..f61ceb7d2 100644
--- a/src/net/slipcor/pvparena/commands/PAI_Info.java
+++ b/src/net/slipcor/pvparena/commands/PAI_Info.java
@@ -30,7 +30,7 @@
 public class PAI_Info extends AbstractArenaCommand {
 
     public PAI_Info() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.info"});
+        super(new String[]{"pvparena.cmds.info"});
     }
 
     @Override
@@ -141,7 +141,7 @@ public void commit(final Arena arena, final CommandSender sender, final String[]
         if (displayMode == null || "perms".equals(displayMode)) {
             arena.msg(sender, Language.parse(arena, MSG.INFO_SECTION, "perms"));
             arena.msg(sender, StringParser.colorVar("explicitarena",
-                    arena.getArenaConfig().getBoolean(CFG.PERMS_EXPLICITARENA)) + " | " +
+                    arena.getArenaConfig().getBoolean(CFG.PERMS_EXPLICIT_PER_ARENA)) + " | " +
                     StringParser.colorVar("explicitclass",
                             arena.getArenaConfig().getBoolean(CFG.PERMS_EXPLICITCLASS)) + " | " +
                     StringParser.colorVar("joininbattle",
diff --git a/src/net/slipcor/pvparena/commands/PAI_List.java b/src/net/slipcor/pvparena/commands/PAI_List.java
index 2a72f35ab..bd327eae3 100644
--- a/src/net/slipcor/pvparena/commands/PAI_List.java
+++ b/src/net/slipcor/pvparena/commands/PAI_List.java
@@ -24,7 +24,7 @@
 public class PAI_List extends AbstractArenaCommand {
 
     public PAI_List() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.list"});
+        super(new String[]{"pvparena.cmds.list"});
     }
 
     private static final Map colorMap = new HashMap<>();
diff --git a/src/net/slipcor/pvparena/commands/PAI_Ready.java b/src/net/slipcor/pvparena/commands/PAI_Ready.java
index d5b0f1623..1c5a30afa 100644
--- a/src/net/slipcor/pvparena/commands/PAI_Ready.java
+++ b/src/net/slipcor/pvparena/commands/PAI_Ready.java
@@ -30,7 +30,7 @@
 public class PAI_Ready extends AbstractArenaCommand {
 
     public PAI_Ready() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.ready"});
+        super(new String[]{"pvparena.cmds.ready"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAI_Shutup.java b/src/net/slipcor/pvparena/commands/PAI_Shutup.java
index dd95f4f81..4786f457e 100644
--- a/src/net/slipcor/pvparena/commands/PAI_Shutup.java
+++ b/src/net/slipcor/pvparena/commands/PAI_Shutup.java
@@ -24,7 +24,7 @@
 public class PAI_Shutup extends AbstractArenaCommand {
 
     public PAI_Shutup() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.shutup"});
+        super(new String[]{"pvparena.cmds.shutup"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAI_Stats.java b/src/net/slipcor/pvparena/commands/PAI_Stats.java
index 862e25942..0c381c57c 100644
--- a/src/net/slipcor/pvparena/commands/PAI_Stats.java
+++ b/src/net/slipcor/pvparena/commands/PAI_Stats.java
@@ -27,7 +27,7 @@
 public class PAI_Stats extends AbstractArenaCommand {
 
     public PAI_Stats() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.stats"});
+        super(new String[]{"pvparena.cmds.stats"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/commands/PAI_Version.java b/src/net/slipcor/pvparena/commands/PAI_Version.java
index e574d756d..db64caeff 100644
--- a/src/net/slipcor/pvparena/commands/PAI_Version.java
+++ b/src/net/slipcor/pvparena/commands/PAI_Version.java
@@ -25,7 +25,7 @@
 public class PAI_Version extends AbstractGlobalCommand {
 
     public PAI_Version() {
-        super(new String[]{"pvparena.user", "pvparena.cmds.version"});
+        super(new String[]{"pvparena.cmds.version"});
     }
 
     @Override
diff --git a/src/net/slipcor/pvparena/core/ColorUtils.java b/src/net/slipcor/pvparena/core/ColorUtils.java
index abe8b17c1..c715178ff 100644
--- a/src/net/slipcor/pvparena/core/ColorUtils.java
+++ b/src/net/slipcor/pvparena/core/ColorUtils.java
@@ -1,15 +1,14 @@
 package net.slipcor.pvparena.core;
 
-import org.bukkit.Bukkit;
-import org.bukkit.ChatColor;
-import org.bukkit.DyeColor;
-import org.bukkit.Material;
+import org.bukkit.*;
 import org.bukkit.block.Block;
 import org.bukkit.block.data.BlockData;
 import org.bukkit.block.data.Directional;
 import org.bukkit.block.data.Rotatable;
+import org.bukkit.inventory.ItemStack;
 
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -104,13 +103,18 @@ public static Material getColoredMaterial(DyeColor dyeColor, Material typeMateri
     }
 
     public static boolean isSubType(Material type, Material check) {
-        return (type == check) || (isColorableMaterial(type) && getMaterialSuffix(type).equals(getMaterialSuffix(check)));
+        return (type == check) || (isColorableMaterial(type) && (getMaterialSuffix(check).equals(getMaterialSuffix(type))));
+    }
+
+    public static boolean isDroppedItemSubType(ItemStack item, Material reference) {
+        Material itemMaterial = item.getType();
+        return isSubType(itemMaterial, reference) || (Tag.BANNERS.isTagged(itemMaterial) && Tag.BANNERS.isTagged(reference));
     }
 
     private static String getMaterialSuffix(Material material) {
         return getColorableSuffixes().stream()
                 .filter(suffix -> material.name().endsWith(suffix))
-                .findFirst()
+                .max(Comparator.comparingInt(String::length)) // get the suffix with the most of common characters
                 .orElse("");
     }
 
@@ -132,7 +136,7 @@ private static List getColorableSuffixes() {
      */
     public static void setNewFlagColor(Block flagBlock, ChatColor flagColor) {
         final BlockData originalBlockData = flagBlock.getBlockData().clone();
-        Material newMaterial = ColorUtils.getColoredMaterialFromChatColor(flagColor, flagBlock.getType());
+        Material newMaterial = ColorUtils.getColoredMaterialFromChatColor(flagColor, originalBlockData.getMaterial());
         BlockData newData = Bukkit.getServer().createBlockData(newMaterial);
 
         if(originalBlockData instanceof Directional) {
diff --git a/src/net/slipcor/pvparena/core/Config.java b/src/net/slipcor/pvparena/core/Config.java
index a6af40978..3a6c5eeab 100644
--- a/src/net/slipcor/pvparena/core/Config.java
+++ b/src/net/slipcor/pvparena/core/Config.java
@@ -82,6 +82,7 @@ public enum CFG {
         GOAL_ADDLIVESPERPLAYER("goal.livesPerPlayer", false, null),
 
         ITEMS_EXCLUDEFROMDROPS("items.excludeFromDrops", new ItemStack[0], null),
+        ITEMS_ONLYDROPS("items.onlyDrops", new ItemStack[0], null),
         ITEMS_KEEPONRESPAWN("items.keepOnRespawn", new ItemStack[0], null),
         ITEMS_KEEPALLONRESPAWN("items.keepAllOnRespawn", false, null),
         ITEMS_MINPLAYERS("items.minplayers", 2, null),
@@ -107,7 +108,7 @@ public enum CFG {
         MSG_YOUJOINEDTEAM("msg.youjoinedteam", "You have joined team %1%!", null),
 
         PERMS_ALWAYSJOININBATTLE("perms.alwaysJoinInBattle", false, null),
-        PERMS_EXPLICITARENA("perms.explicitArenaNeeded", false, null),
+        PERMS_EXPLICIT_PER_ARENA("perms.explicitArenaNeeded", false, null),
         PERMS_EXPLICITCLASS("perms.explicitClassNeeded", false, null),
         PERMS_FLY("perms.fly", false, null),
         PERMS_LOUNGEINTERACT("perms.loungeinteract", false, null),
diff --git a/src/net/slipcor/pvparena/core/Language.java b/src/net/slipcor/pvparena/core/Language.java
index f48c20caa..001c1d441 100644
--- a/src/net/slipcor/pvparena/core/Language.java
+++ b/src/net/slipcor/pvparena/core/Language.java
@@ -127,6 +127,7 @@ public enum MSG {
         ERROR_CLASS_FULL("nulang.error.class.full", "The class &a%1%&r is full!"),
         ERROR_CLASS_NOTENOUGHEXP("nulang.error.class.notenoughexp", "You don't have enough EXP to choose &a%1%&r!"),
         ERROR_CLASS_NOT_FOUND("nulang.error.class.notfound", "Class not found: &a%1%&r"),
+        ERROR_CLASS_NOT_GIVEN("nulang.error.class.notgiven", "No class was given!"),
         ERROR_COMMAND_BLOCKED("nulang.error.cmdblocked", "&cCommand blocked: %1%"),
         ERROR_COMMAND_INVALID("nulang.error.invalidcmd", "Invalid command: %1%"),
         ERROR_COMMAND_UNKNOWN("nulang.error.unknowncmd", "Unknown command"),
@@ -157,7 +158,7 @@ public enum MSG {
         ERROR_NO_GOAL("nulang.error.nogoal", "You did not add a goal! &a/pa [arena] goal [goalname]"),
         ERROR_NO_SPAWNS("nulang.error.nospawns", "No spawns set!"),
         ERROR_NOPERM_CLASS("nulang.error.classperms", "You do not have permission for class &a%1%&r!"),
-        ERROR_NOPERM_JOIN("nulang.error.permjoin", "You don't have permission to join the arena!"),
+        ERROR_NOPERM_JOIN("nulang.error.permjoin", "You don't have permission to join this arena!"),
 
         ERROR_NOPERM_X_ADMIN("nulang.nopermto.madmin", "administrate"),
         ERROR_NOPERM_X_CREATE("nulang.nopermto.create", "create an arena"),
diff --git a/src/net/slipcor/pvparena/goals/GoalBlockDestroy.java b/src/net/slipcor/pvparena/goals/GoalBlockDestroy.java
index d941f51c5..5cbf48c9d 100644
--- a/src/net/slipcor/pvparena/goals/GoalBlockDestroy.java
+++ b/src/net/slipcor/pvparena/goals/GoalBlockDestroy.java
@@ -21,6 +21,7 @@
 import net.slipcor.pvparena.loadables.ArenaModuleManager;
 import net.slipcor.pvparena.loadables.ArenaRegion;
 import net.slipcor.pvparena.loadables.ArenaRegion.RegionType;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.managers.TeamManager;
 import net.slipcor.pvparena.runnables.EndRunnable;
@@ -187,8 +188,8 @@ public PACheck checkSetBlock(final PACheck res, final Player player, final Block
             return res;
         }
 
-        if (!PVPArena.hasAdminPerms(player)
-                && !PVPArena.hasCreatePerms(player, arena)) {
+        if (!PermissionManager.hasAdminPerm(player)
+                && !PermissionManager.hasBuilderPerm(player, arena)) {
             return res;
         }
         res.setPriority(this, PRIORITY); // success :)
diff --git a/src/net/slipcor/pvparena/goals/GoalDomination.java b/src/net/slipcor/pvparena/goals/GoalDomination.java
index 1778a586c..797f2cf6b 100644
--- a/src/net/slipcor/pvparena/goals/GoalDomination.java
+++ b/src/net/slipcor/pvparena/goals/GoalDomination.java
@@ -14,6 +14,7 @@
 import net.slipcor.pvparena.events.PAGoalEvent;
 import net.slipcor.pvparena.loadables.ArenaGoal;
 import net.slipcor.pvparena.loadables.ArenaModuleManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.managers.TeamManager;
 import net.slipcor.pvparena.runnables.CircleParticleRunnable;
@@ -562,8 +563,8 @@ public void commitEnd(final boolean force) {
     @Override
     public boolean commitSetFlag(final Player player, final Block block) {
 
-        if (PVPArena.hasAdminPerms(player)
-                || PVPArena.hasCreatePerms(player, this.arena)
+        if (PermissionManager.hasAdminPerm(player)
+                || PermissionManager.hasBuilderPerm(player, this.arena)
                 && player.getInventory().getItemInMainHand().getType().toString().equals(this.arena
                 .getArenaConfig().getString(CFG.GENERAL_WAND))) {
 
diff --git a/src/net/slipcor/pvparena/goals/GoalFlags.java b/src/net/slipcor/pvparena/goals/GoalFlags.java
index ddb16dd35..cfef7fdb1 100644
--- a/src/net/slipcor/pvparena/goals/GoalFlags.java
+++ b/src/net/slipcor/pvparena/goals/GoalFlags.java
@@ -16,6 +16,7 @@
 import net.slipcor.pvparena.events.PAGoalEvent;
 import net.slipcor.pvparena.loadables.ArenaGoal;
 import net.slipcor.pvparena.loadables.ArenaModuleManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.managers.StatisticsManager.Type;
 import net.slipcor.pvparena.managers.TeamManager;
@@ -428,7 +429,7 @@ public PACheck checkSetBlock(final PACheck res, final Player player, final Block
             return res;
         }
 
-        if (!PVPArena.hasAdminPerms(player) && !PVPArena.hasCreatePerms(player, this.arena)) {
+        if (!PermissionManager.hasAdminPerm(player) && !PermissionManager.hasBuilderPerm(player, this.arena)) {
             return res;
         }
         res.setPriority(this, PRIORITY); // success :)
@@ -511,8 +512,7 @@ public void commitCommand(final CommandSender sender, final String[] args) {
             this.arena.getArenaConfig().set(CFG.GOAL_FLAGS_FLAGTYPE, mat.name());
 
             this.arena.getArenaConfig().save();
-            this.arena.msg(sender, Language.parse(this.arena, MSG.GOAL_FLAGS_TYPESET,
-                    CFG.GOAL_FLAGS_FLAGTYPE.toString()));
+            this.arena.msg(sender, Language.parse(this.arena, MSG.GOAL_FLAGS_TYPESET, mat.name()));
 
         } else if ("flageffect".equalsIgnoreCase(args[0])) {
 
diff --git a/src/net/slipcor/pvparena/goals/GoalFood.java b/src/net/slipcor/pvparena/goals/GoalFood.java
index 42fa66c15..03b6a0646 100644
--- a/src/net/slipcor/pvparena/goals/GoalFood.java
+++ b/src/net/slipcor/pvparena/goals/GoalFood.java
@@ -18,6 +18,7 @@
 import net.slipcor.pvparena.loadables.ArenaGoal;
 import net.slipcor.pvparena.loadables.ArenaModuleManager;
 import net.slipcor.pvparena.managers.InventoryManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.managers.TeamManager;
 import net.slipcor.pvparena.runnables.EndRunnable;
@@ -193,8 +194,8 @@ public PACheck checkSetBlock(final PACheck res, final Player player, final Block
             return res;
         }
 
-        if (!PVPArena.hasAdminPerms(player)
-                && !PVPArena.hasCreatePerms(player, arena)) {
+        if (!PermissionManager.hasAdminPerm(player)
+                && !PermissionManager.hasBuilderPerm(player, arena)) {
             return res;
         }
         res.setPriority(this, PRIORITY); // success :)
@@ -549,19 +550,19 @@ public void parseStart() {
                 pos--;
             }
             int totalAmount = pAmount;
-            totalAmount += tAmount / team.getTeamMembers().size();
+            totalAmount += tAmount / Math.max(team.getTeamMembers().size(), 1);
 
             if (totalAmount < 1) {
                 totalAmount = 1;
             }
+
             for (final ArenaPlayer player : team.getTeamMembers()) {
 
                 player.get().getInventory().addItem(new ItemStack(getFoodMap().get(team), totalAmount));
                 player.get().updateInventory();
             }
             chestMap.put(SpawnManager.getBlockByExactName(arena, team.getName() + "foodchest").toLocation().getBlock(), team);
-            getLifeMap().put(team.getName(),
-                    arena.getArenaConfig().getInt(CFG.GOAL_FOOD_FMAXITEMS));
+            getLifeMap().put(team.getName(), arena.getArenaConfig().getInt(CFG.GOAL_FOOD_FMAXITEMS));
         }
     }
 
diff --git a/src/net/slipcor/pvparena/goals/GoalLiberation.java b/src/net/slipcor/pvparena/goals/GoalLiberation.java
index 9c69998e8..032138c2d 100644
--- a/src/net/slipcor/pvparena/goals/GoalLiberation.java
+++ b/src/net/slipcor/pvparena/goals/GoalLiberation.java
@@ -16,6 +16,7 @@
 import net.slipcor.pvparena.loadables.ArenaGoal;
 import net.slipcor.pvparena.loadables.ArenaModuleManager;
 import net.slipcor.pvparena.managers.InventoryManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.managers.TeamManager;
 import net.slipcor.pvparena.runnables.EndRunnable;
@@ -275,8 +276,8 @@ public PACheck checkSetBlock(final PACheck res, final Player player, final Block
             return res;
         }
 
-        if (!PVPArena.hasAdminPerms(player)
-                && !PVPArena.hasCreatePerms(player, arena)) {
+        if (!PermissionManager.hasAdminPerm(player)
+                && !PermissionManager.hasBuilderPerm(player, arena)) {
             return res;
         }
         res.setPriority(this, PRIORITY); // success :)
diff --git a/src/net/slipcor/pvparena/goals/GoalPhysicalFlags.java b/src/net/slipcor/pvparena/goals/GoalPhysicalFlags.java
index a70793568..8148bff3a 100644
--- a/src/net/slipcor/pvparena/goals/GoalPhysicalFlags.java
+++ b/src/net/slipcor/pvparena/goals/GoalPhysicalFlags.java
@@ -16,6 +16,7 @@
 import net.slipcor.pvparena.events.PAGoalEvent;
 import net.slipcor.pvparena.loadables.ArenaGoal;
 import net.slipcor.pvparena.loadables.ArenaModuleManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.managers.TeamManager;
 import net.slipcor.pvparena.runnables.EndRunnable;
@@ -205,7 +206,7 @@ public PACheck checkInteract(final PACheck res, final Player player, final Block
                 this.arena.getDebugger().i("the flag belongs to team " + flagTeam, player);
 
                 ItemStack mainHandItem = player.getInventory().getItemInMainHand();
-                if (!ColorUtils.isSubType(mainHandItem.getType(), flagType)) {
+                if (!ColorUtils.isDroppedItemSubType(mainHandItem, flagType)) {
                     this.arena.getDebugger().i("player " + player.getName() + " is not holding the flag", player);
                     this.arena.msg(player, Language.parse(this.arena, MSG.GOAL_PHYSICALFLAGS_HOLDFLAG));
                     return res;
@@ -310,7 +311,7 @@ private void applyEffects(final Player player) {
             return;
         }
 
-        player.addPotionEffect(new PotionEffect(pet, amp, 2147000));
+        player.addPotionEffect(new PotionEffect(pet, 2147000, amp));
     }
 
     @Override
@@ -358,7 +359,7 @@ public PACheck checkSetBlock(final PACheck res, final Player player, final Block
             return res;
         }
 
-        if (!PVPArena.hasAdminPerms(player) && !PVPArena.hasCreatePerms(player, this.arena)) {
+        if (!PermissionManager.hasAdminPerm(player) && !PermissionManager.hasBuilderPerm(player, this.arena)) {
             return res;
         }
         res.setPriority(this, PRIORITY); // success :)
@@ -441,8 +442,7 @@ public void commitCommand(final CommandSender sender, final String[] args) {
             this.arena.getArenaConfig().set(CFG.GOAL_PFLAGS_FLAGTYPE, mat.name());
 
             this.arena.getArenaConfig().save();
-            this.arena.msg(sender, Language.parse(this.arena, MSG.GOAL_FLAGS_TYPESET,
-                    CFG.GOAL_PFLAGS_FLAGTYPE.toString()));
+            this.arena.msg(sender, Language.parse(this.arena, MSG.GOAL_FLAGS_TYPESET, mat.name()));
 
         } else if ("flageffect".equalsIgnoreCase(args[0])) {
 
@@ -1026,7 +1026,7 @@ public void onFlagClaim(final BlockBreakEvent event) {
                 }
                 this.applyEffects(player);
                 this.getFlagMap().put(teamName, player.getName());
-                player.getInventory().addItem(new ItemStack(block.getType()));
+                player.getInventory().addItem(block.getDrops().toArray(new ItemStack[0]));
                 block.setType(Material.AIR);;
                 event.setCancelled(true);
                 return;
diff --git a/src/net/slipcor/pvparena/goals/GoalPlayerKillReward.java b/src/net/slipcor/pvparena/goals/GoalPlayerKillReward.java
index 09d910d05..d4221b0e5 100644
--- a/src/net/slipcor/pvparena/goals/GoalPlayerKillReward.java
+++ b/src/net/slipcor/pvparena/goals/GoalPlayerKillReward.java
@@ -32,6 +32,7 @@
 
 import java.util.*;
 
+import static net.slipcor.pvparena.core.ItemStackUtils.getItemStacksFromConfig;
 import static net.slipcor.pvparena.core.Utils.getSerializableItemStacks;
 
 /**
@@ -454,17 +455,18 @@ public void setDefaults(final YamlConfiguration config) {
         }
 
 
-        final ConfigurationSection cs = (ConfigurationSection) config
-                .get("goal.playerkillrewards");
+        ConfigurationSection cs = (ConfigurationSection) config.get("goal.playerkillrewards");
 
         if (cs != null) {
-            for (final String line : cs.getKeys(false)) {
+            for (String line : cs.getKeys(false)) {
                 try {
-                    this.getItemMap().put(Integer.parseInt(line.substring(2)),
+                    ConfigurationSection classesCfg = cs.getConfigurationSection(line);
+                    int classIndex = Integer.parseInt(line.substring(2));
+                    this.getItemMap().put(classIndex,
                         new ItemStack[][] {
-                            cs.getList(line + ".items").toArray(new ItemStack[0]),
-                            cs.getList(line + ".offhand").toArray(new ItemStack[]{new ItemStack(Material.AIR, 1)}),
-                            cs.getList(line + ".armor").toArray(new ItemStack[0])
+                            getItemStacksFromConfig(classesCfg.getList("items")),
+                            getItemStacksFromConfig(classesCfg.getList("offhand")),
+                            getItemStacksFromConfig(classesCfg.getList("armor")),
                         });
                 } catch (final Exception ignored) {
                 }
diff --git a/src/net/slipcor/pvparena/goals/GoalSabotage.java b/src/net/slipcor/pvparena/goals/GoalSabotage.java
index 8a6d71b59..8e0478555 100644
--- a/src/net/slipcor/pvparena/goals/GoalSabotage.java
+++ b/src/net/slipcor/pvparena/goals/GoalSabotage.java
@@ -16,6 +16,7 @@
 import net.slipcor.pvparena.events.PAGoalEvent;
 import net.slipcor.pvparena.loadables.ArenaGoal;
 import net.slipcor.pvparena.loadables.ArenaModuleManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.managers.StatisticsManager.Type;
 import net.slipcor.pvparena.runnables.EndRunnable;
@@ -259,8 +260,8 @@ public PACheck checkSetBlock(final PACheck res, final Player player, final Block
             return res;
         }
 
-        if (!PVPArena.hasAdminPerms(player)
-                && !PVPArena.hasCreatePerms(player, this.arena)) {
+        if (!PermissionManager.hasAdminPerm(player)
+                && !PermissionManager.hasBuilderPerm(player, this.arena)) {
             return res;
         }
         res.setPriority(this, PRIORITY); // success :)
diff --git a/src/net/slipcor/pvparena/listeners/PlayerListener.java b/src/net/slipcor/pvparena/listeners/PlayerListener.java
index c096b4e92..33efb8bdc 100644
--- a/src/net/slipcor/pvparena/listeners/PlayerListener.java
+++ b/src/net/slipcor/pvparena/listeners/PlayerListener.java
@@ -25,6 +25,7 @@
 import net.slipcor.pvparena.loadables.ArenaRegion.RegionType;
 import net.slipcor.pvparena.managers.ArenaManager;
 import net.slipcor.pvparena.managers.InventoryManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.managers.TeamManager;
 import org.bukkit.Bukkit;
@@ -81,7 +82,7 @@ private boolean checkAndCommitCancel(final Arena arena, final Player player,
         }
 
         DEBUG.i("checkAndCommitCancel", player);
-        if (arena == null || player.hasPermission("pvparena.admin")) {
+        if (arena == null || PermissionManager.hasAdminPerm(player)) {
             DEBUG.i("no arena or admin", player);
             DEBUG.i("> false", player);
             return false;
@@ -211,8 +212,8 @@ public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event)
         }
 
         final Arena arena = ArenaPlayer.parsePlayer(player.getName()).getArena();
-        if (arena == null || player.isOp() || PVPArena.hasAdminPerms(player)
-                || PVPArena.hasCreatePerms(player, arena)) {
+        if (arena == null || player.isOp() || PermissionManager.hasAdminPerm(player)
+                || PermissionManager.hasBuilderPerm(player, arena)) {
             return; // no fighting player => OUT
         }
 
@@ -267,8 +268,8 @@ public void onPlayerCraft(final CraftItemEvent event) {
         final Player player = (Player) event.getWhoClicked();
 
         final Arena arena = ArenaPlayer.parsePlayer(player.getName()).getArena();
-        if (arena == null || player.isOp() || PVPArena.hasAdminPerms(player)
-                || PVPArena.hasCreatePerms(player, arena)) {
+        if (arena == null || player.isOp() || PermissionManager.hasAdminPerm(player)
+                || PermissionManager.hasBuilderPerm(player, arena)) {
             return; // no fighting player => OUT
         }
 
@@ -898,7 +899,7 @@ public void onPlayerTeleport(final PlayerTeleportEvent event) {
         final Set regions = arena
                 .getRegionsByType(RegionType.BATTLE);
 
-        if (regions == null || regions.size() < 0) {
+        if (regions == null || regions.isEmpty()) {
             maybeFixInvisibility(arena, player);
 
             return;
@@ -971,4 +972,4 @@ public void onPlayerVelocity(final ProjectileLaunchEvent event) {
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/src/net/slipcor/pvparena/loadables/ArenaGoal.java b/src/net/slipcor/pvparena/loadables/ArenaGoal.java
index 98058df74..36f5616fb 100644
--- a/src/net/slipcor/pvparena/loadables/ArenaGoal.java
+++ b/src/net/slipcor/pvparena/loadables/ArenaGoal.java
@@ -1,6 +1,5 @@
 package net.slipcor.pvparena.loadables;
 
-import net.slipcor.pvparena.PVPArena;
 import net.slipcor.pvparena.api.IArenaCommandHandler;
 import net.slipcor.pvparena.arena.Arena;
 import net.slipcor.pvparena.arena.ArenaPlayer;
@@ -10,6 +9,7 @@
 import net.slipcor.pvparena.core.Config.CFG;
 import net.slipcor.pvparena.core.Debug;
 import net.slipcor.pvparena.core.Language;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.ncloader.NCBLoadable;
 import org.bukkit.ChatColor;
 import org.bukkit.block.Block;
@@ -87,11 +87,11 @@ public CommandTree getSubs(final Arena arena) {
     }
 
     @Override
-    public boolean hasPerms(final CommandSender sender, final Arena arena) {
+    public boolean hasPerms(final CommandSender sender, final Arena arena, final boolean silent) {
         if (arena == null) {
-            return PVPArena.hasAdminPerms(sender);
+            return PermissionManager.hasAdminPerm(sender);
         }
-        return PVPArena.hasAdminPerms(sender) || PVPArena.hasCreatePerms(sender, arena);
+        return PermissionManager.hasAdminPerm(sender) || PermissionManager.hasBuilderPerm(sender, arena);
     }
 
     /**
diff --git a/src/net/slipcor/pvparena/loadables/ArenaModule.java b/src/net/slipcor/pvparena/loadables/ArenaModule.java
index b6999d904..960fade28 100644
--- a/src/net/slipcor/pvparena/loadables/ArenaModule.java
+++ b/src/net/slipcor/pvparena/loadables/ArenaModule.java
@@ -1,6 +1,5 @@
 package net.slipcor.pvparena.loadables;
 
-import net.slipcor.pvparena.PVPArena;
 import net.slipcor.pvparena.api.IArenaCommandHandler;
 import net.slipcor.pvparena.arena.Arena;
 import net.slipcor.pvparena.arena.ArenaClass;
@@ -10,6 +9,7 @@
 import net.slipcor.pvparena.commands.CommandTree;
 import net.slipcor.pvparena.core.Debug;
 import net.slipcor.pvparena.loadables.ArenaRegion.RegionType;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.ncloader.NCBLoadable;
 import org.bukkit.Material;
 import org.bukkit.block.Block;
@@ -99,11 +99,11 @@ public CommandTree getSubs(final Arena arena) {
     }
 
     @Override
-    public boolean hasPerms(final CommandSender sender, final Arena arena) {
+    public boolean hasPerms(final CommandSender sender, final Arena arena, final boolean silent) {
         if (arena == null) {
-            return PVPArena.hasAdminPerms(sender);
+            return PermissionManager.hasAdminPerm(sender);
         }
-        return PVPArena.hasAdminPerms(sender) || PVPArena.hasCreatePerms(sender, arena);
+        return PermissionManager.hasAdminPerm(sender) || PermissionManager.hasBuilderPerm(sender, arena);
     }
 
     /**
diff --git a/src/net/slipcor/pvparena/loadables/ArenaRegion.java b/src/net/slipcor/pvparena/loadables/ArenaRegion.java
index 8aa4a4812..d137d267e 100644
--- a/src/net/slipcor/pvparena/loadables/ArenaRegion.java
+++ b/src/net/slipcor/pvparena/loadables/ArenaRegion.java
@@ -15,6 +15,7 @@
 import net.slipcor.pvparena.core.Language.MSG;
 import net.slipcor.pvparena.listeners.PlayerListener;
 import net.slipcor.pvparena.managers.ArenaManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import net.slipcor.pvparena.runnables.RegionRunnable;
 import org.bukkit.Bukkit;
@@ -199,7 +200,7 @@ public static boolean checkRegionSetPosition(final PlayerInteractEvent event,
         }
         final Arena arena = PAA_Region.activeSelections.get(player.getName());
         if (arena != null
-                && (PVPArena.hasAdminPerms(player) || PVPArena.hasCreatePerms(
+                && (PermissionManager.hasAdminPerm(player) || PermissionManager.hasBuilderPerm(
                 player, arena))
                 && player.getEquipment().getItemInMainHand() != null
                 && player.getEquipment().getItemInMainHand().getType().toString().equals(arena
diff --git a/src/net/slipcor/pvparena/managers/ArenaManager.java b/src/net/slipcor/pvparena/managers/ArenaManager.java
index cbfa8a2ae..d271ccdb4 100644
--- a/src/net/slipcor/pvparena/managers/ArenaManager.java
+++ b/src/net/slipcor/pvparena/managers/ArenaManager.java
@@ -389,16 +389,14 @@ public static void trySignJoin(final PlayerInteractEvent event, final Player pla
                     final String sName = sign.getLine(1).toLowerCase();
                     String[] newArgs = new String[0];
                     final Arena arena = ARENAS.get(sName);
-                    if (sign.getLine(2) != null
-                            && arena.getTeam(sign.getLine(2)) != null) {
-                        newArgs = new String[1];
-                        newArgs[0] = sign.getLine(2);
-                    }
                     if (arena == null) {
-                        Arena.pmsg(player,
-                                Language.parse(MSG.ERROR_ARENA_NOTFOUND, sName));
+                        Arena.pmsg(player, Language.parse(MSG.ERROR_ARENA_NOTFOUND, sName));
                         return;
                     }
+                    if (sign.getLine(2) != null && arena.getTeam(sign.getLine(2)) != null) {
+                        newArgs = new String[1];
+                        newArgs[0] = sign.getLine(2);
+                    }
                     final AbstractArenaCommand command = new PAG_Join();
                     command.commit(arena, player, newArgs);
                 }
@@ -524,7 +522,7 @@ public static String getIndirectArenaName(final Arena arena) {
 
     public static Arena getIndirectArenaByName(final CommandSender sender, String string) {
         DEBUG.i("getIndirect(" + sender.getName() + "): " + string);
-        if (!usingShortcuts || PVPArena.hasOverridePerms(sender)) {
+        if (!usingShortcuts || PermissionManager.hasOverridePerm(sender)) {
             DEBUG.i("out1");
             return getArenaByName(string);
         }
diff --git a/src/net/slipcor/pvparena/managers/ConfigurationManager.java b/src/net/slipcor/pvparena/managers/ConfigurationManager.java
index 966cd23c5..52d69cea0 100644
--- a/src/net/slipcor/pvparena/managers/ConfigurationManager.java
+++ b/src/net/slipcor/pvparena/managers/ConfigurationManager.java
@@ -129,34 +129,7 @@ public static boolean configParse(final Arena arena, final Config cfg) {
 
         if (config.get("classitems") == null) {
             if (PVPArena.instance.getConfig().get("classitems") == null) {
-                config.addDefault("classitems.Ranger.items",
-                        Utils.getItemStacksFromMaterials(Material.BOW, Material.ARROW));
-                config.addDefault("classitems.Ranger.offhand",
-                        Utils.getItemStacksFromMaterials(Material.AIR));
-                config.addDefault("classitems.Ranger.armor",
-                        Utils.getItemStacksFromMaterials(Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS));
-
-                config.addDefault("classitems.Swordsman.items",
-                        Utils.getItemStacksFromMaterials(Material.DIAMOND_SWORD));
-                config.addDefault("classitems.Swordsman.offhand",
-                        Utils.getItemStacksFromMaterials(Material.AIR));
-                config.addDefault("classitems.Swordsman.armor",
-                        Utils.getItemStacksFromMaterials(Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS));
-
-                config.addDefault("classitems.Tank.items",
-                        Utils.getItemStacksFromMaterials(Material.STONE_SWORD));
-                config.addDefault("classitems.Tank.offhand",
-                        Utils.getItemStacksFromMaterials(Material.AIR));
-                config.addDefault("classitems.Tank.armor",
-                        Utils.getItemStacksFromMaterials(Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS));
-
-                config.addDefault("classitems.Pyro.items",
-                        Utils.getItemStacksFromMaterials(Material.FLINT_AND_STEEL, Material.TNT, Material.TNT, Material.TNT));
-                config.addDefault("classitems.Pyro.offhand",
-                        Utils.getItemStacksFromMaterials(Material.AIR));
-                config.addDefault("classitems.Pyro.armor",
-                        Utils.getItemStacksFromMaterials(Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS));
-
+                config.addDefault("classitems", generateDefaultClasses());
             }
         }
 
@@ -198,8 +171,8 @@ public static boolean configParse(final Arena arena, final Config cfg) {
         ArenaClass.addGlobalClasses(arena);
         for (final Map.Entry stringObjectEntry1 : classes.entrySet()) {
             ItemStack[] items;
-            ItemStack offHand = new ItemStack(Material.AIR, 1);
-            ItemStack[] armors = new ItemStack[]{new ItemStack(Material.AIR, 1)};
+            ItemStack offHand;
+            ItemStack[] armors;
 
             try {
                 items = getItemStacksFromConfig(config.getList("classitems."+stringObjectEntry1.getKey()+".items"));
@@ -303,6 +276,36 @@ public static boolean configParse(final Arena arena, final Config cfg) {
         return true;
     }
 
+    public static Map generateDefaultClasses() {
+        Map classItems = new HashMap<>();
+
+        classItems.put("Ranger", new HashMap>() {{
+            put("items", Utils.getItemStacksFromMaterials(Material.BOW, Material.ARROW));
+            put("offhand", Utils.getItemStacksFromMaterials(Material.AIR));
+            put("armor", Utils.getItemStacksFromMaterials(Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS));
+        }});
+
+        classItems.put("Swordsman", new HashMap>() {{
+            put("items", Utils.getItemStacksFromMaterials(Material.DIAMOND_SWORD));
+            put("offhand", Utils.getItemStacksFromMaterials(Material.AIR));
+            put("armor", Utils.getItemStacksFromMaterials(Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS));
+        }});
+
+        classItems.put("Tank", new HashMap>() {{
+            put("items", Utils.getItemStacksFromMaterials(Material.STONE_SWORD));
+            put("offhand", Utils.getItemStacksFromMaterials(Material.AIR));
+            put("armor", Utils.getItemStacksFromMaterials(Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS));
+        }});
+
+        classItems.put("Pyro", new HashMap>() {{
+            put("items", Utils.getItemStacksFromMaterials(Material.FLINT_AND_STEEL, Material.TNT, Material.TNT, Material.TNT));
+            put("offhand", Utils.getItemStacksFromMaterials(Material.AIR));
+            put("armor", Utils.getItemStacksFromMaterials(Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS));
+        }});
+
+        return classItems;
+    }
+
     /**
      * check if an arena is configured completely
      *
diff --git a/src/net/slipcor/pvparena/managers/InventoryManager.java b/src/net/slipcor/pvparena/managers/InventoryManager.java
index 33c92c4b6..2133c5d72 100644
--- a/src/net/slipcor/pvparena/managers/InventoryManager.java
+++ b/src/net/slipcor/pvparena/managers/InventoryManager.java
@@ -62,6 +62,7 @@ public static List drop(final Player player) {
 
         DEBUG.i("dropping player inventory: " + player.getName(), player);
         final List exclude;
+        final List only;
         final List keep;
 
         final ArenaPlayer ap = ArenaPlayer.parsePlayer(player.getName());
@@ -71,14 +72,22 @@ public static List drop(final Player player) {
         if (ap == null || ap.getArena() == null) {
             exclude = new ArrayList<>();
             keep = new ArrayList<>();
+            only = new ArrayList<>();
         } else {
-            final ItemStack[] items = ap.getArena().getArenaConfig().getItems(CFG.ITEMS_EXCLUDEFROMDROPS);
+            final ItemStack[] itemsExcluded = ap.getArena().getArenaConfig().getItems(CFG.ITEMS_EXCLUDEFROMDROPS);
             exclude = new ArrayList<>();
-            for (final ItemStack item : items) {
+            for (final ItemStack item : itemsExcluded) {
                 if (item != null) {
                     exclude.add(item.getType());
                 }
             }
+            final ItemStack[] itemsOnlyDrop = ap.getArena().getArenaConfig().getItems(CFG.ITEMS_ONLYDROPS);
+            only = new ArrayList<>();
+            for (final ItemStack item : itemsOnlyDrop) {
+                if (item != null) {
+                    only.add(item.getType());
+                }
+            }
             keepAll = ap.getArena().getArenaConfig().getBoolean(CFG.ITEMS_KEEPALLONRESPAWN);
             if (keepAll) {
                 keep = new ArrayList<>();
@@ -107,11 +116,14 @@ public static List drop(final Player player) {
             if (exclude.contains(is.getType())) {
                 continue;
             }
-            if (keepAll) {
+            if (keepAll && (only.isEmpty() || !only.contains(is.getType()))) {
                 returned.add(is.clone());
                 continue;
             }
-            player.getWorld().dropItemNaturally(player.getLocation(), is);
+            if (only.isEmpty() || only.contains(is.getType())) {
+                DEBUG.i("Natural drop of: " + is.getType().name(), player);
+                player.getWorld().dropItemNaturally(player.getLocation(), is);
+            }
         }
         player.getInventory().clear();
         ap.setMayDropInventory(false);
diff --git a/src/net/slipcor/pvparena/managers/PermissionManager.java b/src/net/slipcor/pvparena/managers/PermissionManager.java
new file mode 100644
index 000000000..575b62c1a
--- /dev/null
+++ b/src/net/slipcor/pvparena/managers/PermissionManager.java
@@ -0,0 +1,110 @@
+package net.slipcor.pvparena.managers;
+
+import net.slipcor.pvparena.PVPArena;
+import net.slipcor.pvparena.arena.Arena;
+import net.slipcor.pvparena.arena.ArenaClass;
+import net.slipcor.pvparena.core.Config;
+import net.slipcor.pvparena.core.Language;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class PermissionManager {
+
+    public static final String CREATE_PERM = "pvparena.create";
+    public static final String ADMIN_PERM = "pvparena.admin";
+
+    public static final String PERM_NODE_PREFIX = "nulang.nopermto.";
+
+
+    /**
+     * Get the permission deny message (translated and colored)
+     *
+     * @param perm permission string
+     * @return message to display to user
+     */
+    public static String getMissingPermissionMessage(String perm) {
+        String permString = perm.substring(perm.indexOf(".") + 1);
+        Language.MSG node = Language.MSG.getByNode(PERM_NODE_PREFIX + permString);
+        if (node != null) {
+            return Language.parse(Language.MSG.ERROR_NOPERM, Language.parse(node));
+        } else {
+            return Language.parse(Language.MSG.ERROR_NOPERM, Language.parse(Language.MSG.ERROR_NOPERM_X_USER));
+        }
+    }
+
+    /**
+     * Allowed player to administrate the arena if he's the owner
+     *
+     * @param sender sender
+     * @param arena  arena
+     * @return true if player has builder permission for this arena
+     */
+    public static boolean hasBuilderPerm(CommandSender sender, Arena arena) {
+        return sender.hasPermission(CREATE_PERM)
+                && sender.getName().equals(arena.getOwner());
+    }
+
+    /**
+     * Check if a CommandSender has admin permissions
+     *
+     * @param sender the CommandSender to check
+     * @return true if a CommandSender has admin permissions, false otherwise
+     */
+    public static boolean hasAdminPerm(CommandSender sender) {
+        return sender.hasPermission(ADMIN_PERM);
+    }
+
+
+    /**
+     * Check if a CommandSender has permission for an arena
+     *
+     * @param sender the CommandSender to check
+     * @param arena  the arena to check
+     * @return true if explicit permission not needed or granted, false
+     * otherwise
+     */
+    public static boolean hasExplicitArenaPerm(CommandSender sender, Arena arena, String command) {
+        if (arena.getArenaConfig().getBoolean(Config.CFG.PERMS_EXPLICIT_PER_ARENA)) {
+            final String perm = String.format("pvparena.%s.%s", command, arena.getName().toLowerCase());
+            arena.getDebugger().i(
+                    " - explicit arena perm: " + sender.hasPermission(perm), sender);
+
+            return sender.hasPermission(perm);
+        }
+
+        // explicit permissions not enabled
+        return true;
+    }
+
+    /**
+     * Check if a CommandSender has permission for an class
+     *
+     * @param sender the CommandSender to check
+     * @param arena  the arena to check
+     * @param aClass the class
+     * @return true if explicit permission not needed or granted, false
+     * otherwise
+     */
+    public static boolean hasExplicitClassPerm(CommandSender sender, Arena arena, ArenaClass aClass) {
+
+        if (arena.getArenaConfig().getBoolean(Config.CFG.PERMS_EXPLICITCLASS)) {
+            final String perm = String.format("pvparena.class.%s", aClass.getName().toLowerCase());
+            arena.getDebugger().i(
+                    " - explicit class perm: " + sender.hasPermission(perm), sender);
+
+            return sender.hasPermission(perm);
+        }
+
+        // explicit permissions not enabled
+        return true;
+    }
+
+    public static boolean hasOverridePerm(final CommandSender sender) {
+        if (sender instanceof Player) {
+            return sender.hasPermission("pvparena.override");
+        }
+
+        return PVPArena.instance.getConfig().getBoolean("consoleoffduty")
+                != sender.hasPermission("pvparena.override");
+    }
+}
diff --git a/src/net/slipcor/pvparena/managers/TabManager.java b/src/net/slipcor/pvparena/managers/TabManager.java
index 3bab4489a..54f0fd422 100644
--- a/src/net/slipcor/pvparena/managers/TabManager.java
+++ b/src/net/slipcor/pvparena/managers/TabManager.java
@@ -121,7 +121,7 @@ public static List getMatches(final CommandSender sender, final List matches, final CommandSender sender, final Arena arena, final List list, final String prefix) {
         for (final IArenaCommandHandler ach : list) {
-            if (ach.hasPerms(sender, arena)) {
+            if (ach.hasPerms(sender, arena, true)) {
                 if(prefix.startsWith("!") || prefix.startsWith("-")) {
                     for (final String value : ach.getShort()) {
                         if (startsWithIgnoreCase(value, prefix)) {
@@ -372,4 +372,4 @@ private static String getOverrideKey(final String key, final String definition,
         }
         return key;
     }
-}
\ No newline at end of file
+}
diff --git a/src/net/slipcor/pvparena/modules/BattlefieldJoin.java b/src/net/slipcor/pvparena/modules/BattlefieldJoin.java
index dc342c798..8a1f640e9 100644
--- a/src/net/slipcor/pvparena/modules/BattlefieldJoin.java
+++ b/src/net/slipcor/pvparena/modules/BattlefieldJoin.java
@@ -14,6 +14,7 @@
 import net.slipcor.pvparena.core.Language.MSG;
 import net.slipcor.pvparena.loadables.ArenaModule;
 import net.slipcor.pvparena.managers.ArenaManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.managers.SpawnManager;
 import org.bukkit.Bukkit;
 import org.bukkit.command.CommandSender;
@@ -66,9 +67,8 @@ public PACheck checkJoin(final CommandSender sender, final PACheck result, final
         }
 
         if (arena.isLocked()
-                && !player.hasPermission("pvparena.admin")
-                && !(player.hasPermission("pvparena.create") && arena.getOwner()
-                .equals(player.getName()))) {
+                && !PermissionManager.hasAdminPerm(player)
+                && !PermissionManager.hasBuilderPerm(player, arena)) {
             result.setError(this, Language.parse(arena, MSG.ERROR_DISABLED));
             return result;
         }
@@ -122,9 +122,9 @@ public void commitJoin(final Player sender, final ArenaTeam team) {
 
             final Arena arena = player.getArena();
 
-
-            player.createState(player.get());
+            // important: clear inventory before setting player state to deal with armor modifiers (like health).
             ArenaPlayer.backupAndClearInventory(arena, player.get());
+            player.createState(player.get());
             player.dump();
 
 
@@ -172,4 +172,4 @@ public void reset(boolean force) {
     public boolean isInternal() {
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/src/net/slipcor/pvparena/modules/RegionTool.java b/src/net/slipcor/pvparena/modules/RegionTool.java
index cef900fe5..698c391b1 100644
--- a/src/net/slipcor/pvparena/modules/RegionTool.java
+++ b/src/net/slipcor/pvparena/modules/RegionTool.java
@@ -8,6 +8,7 @@
 import net.slipcor.pvparena.loadables.ArenaModule;
 import net.slipcor.pvparena.loadables.ArenaRegion;
 import net.slipcor.pvparena.managers.ArenaManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import org.bukkit.ChatColor;
 import org.bukkit.Material;
 import org.bukkit.event.player.PlayerInteractEvent;
@@ -48,7 +49,7 @@ public boolean onPlayerInteract(final PlayerInteractEvent event) {
             return false;
         }
 
-        if (!PVPArena.hasAdminPerms(event.getPlayer())) {
+        if (!PermissionManager.hasAdminPerm(event.getPlayer())) {
             return false;
         }
 
diff --git a/src/net/slipcor/pvparena/modules/StandardLounge.java b/src/net/slipcor/pvparena/modules/StandardLounge.java
index f6d77e668..8d6e67c07 100644
--- a/src/net/slipcor/pvparena/modules/StandardLounge.java
+++ b/src/net/slipcor/pvparena/modules/StandardLounge.java
@@ -13,6 +13,7 @@
 import net.slipcor.pvparena.core.Language.MSG;
 import net.slipcor.pvparena.loadables.ArenaModule;
 import net.slipcor.pvparena.managers.ArenaManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import org.bukkit.ChatColor;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
@@ -96,10 +97,8 @@ public PACheck checkJoin(final CommandSender sender, final PACheck result, final
             // handle that? ignore!
         }
 
-        if (arena.isLocked()
-                && !player.hasPermission("pvparena.admin")
-                && !(player.hasPermission("pvparena.create") && arena.getOwner()
-                .equals(player.getName()))) {
+        if (arena.isLocked() && !PermissionManager.hasAdminPerm(player)
+                && !PermissionManager.hasBuilderPerm(player, arena)) {
             result.setError(this, Language.parse(arena, MSG.ERROR_DISABLED));
             return result;
         }
@@ -222,8 +221,10 @@ public void commitJoin(final Player sender, final ArenaTeam team) {
 
             final Arena arena = player.getArena();
 
-            player.createState(player.get());
+             // important: clear inventory before setting player state to deal with armor modifiers (like health).
             ArenaPlayer.backupAndClearInventory(arena, player.get());
+            player.createState(player.get());
+            
             player.dump();
 
 
@@ -247,4 +248,4 @@ public void parseJoin(final CommandSender player, final ArenaTeam team) {
             arena.countDown();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/net/slipcor/pvparena/modules/StandardSpectate.java b/src/net/slipcor/pvparena/modules/StandardSpectate.java
index 746e3a73e..7204c12b2 100644
--- a/src/net/slipcor/pvparena/modules/StandardSpectate.java
+++ b/src/net/slipcor/pvparena/modules/StandardSpectate.java
@@ -78,8 +78,9 @@ public void commitSpectate(final Player player) {
 
             final Arena arena = aPlayer.getArena();
 
-            aPlayer.createState(player);
+            // important: clear inventory before setting player state to deal with armor modifiers (like health).
             ArenaPlayer.backupAndClearInventory(arena, player);
+            aPlayer.createState(player);
             aPlayer.dump();
 
 
@@ -108,4 +109,4 @@ public boolean hasSpawn(final String string) {
     public boolean isInternal() {
         return true;
     }
-}
\ No newline at end of file
+}
diff --git a/src/net/slipcor/pvparena/modules/WarmupJoin.java b/src/net/slipcor/pvparena/modules/WarmupJoin.java
index 90907a5c2..fd787baec 100644
--- a/src/net/slipcor/pvparena/modules/WarmupJoin.java
+++ b/src/net/slipcor/pvparena/modules/WarmupJoin.java
@@ -11,6 +11,7 @@
 import net.slipcor.pvparena.core.Language.MSG;
 import net.slipcor.pvparena.loadables.ArenaModule;
 import net.slipcor.pvparena.managers.ArenaManager;
+import net.slipcor.pvparena.managers.PermissionManager;
 import net.slipcor.pvparena.runnables.ArenaWarmupRunnable;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
@@ -67,7 +68,7 @@ public PACheck checkJoin(final CommandSender sender, final PACheck result, final
         }
 
 
-        if (arena.isLocked() && !player.hasPermission("pvparena.admin") && !(player.hasPermission("pvparena.create") && arena.getOwner().equals(player.getName()))) {
+        if (arena.isLocked() && !PermissionManager.hasAdminPerm(player) && !PermissionManager.hasBuilderPerm(player, arena)) {
             result.setError(this, Language.parse(arena, MSG.ERROR_DISABLED));
             return result;
         }
diff --git a/src/net/slipcor/pvparena/updater/ModulesUpdater.java b/src/net/slipcor/pvparena/updater/ModulesUpdater.java
index e684fc3c3..bfaa658a4 100644
--- a/src/net/slipcor/pvparena/updater/ModulesUpdater.java
+++ b/src/net/slipcor/pvparena/updater/ModulesUpdater.java
@@ -46,6 +46,11 @@ protected void runUpdater() throws IOException {
         JsonObject versionJson = getVersionJson(connection.getInputStream());
         String onlineVersion = getOnlineVersionFromJson(versionJson);
 
+        if(Long.parseLong(onlineVersion.split("\\.")[0]) > 1) {
+            // Ignore modules update if 2.0 has been detected
+            return;
+        }
+
         if(currentVersion == null) {
             LOG.info("PVP Arena modules are not detected");
         } else if(isUpToDate(currentVersion, onlineVersion)) {
diff --git a/src/net/slipcor/pvparena/updater/PluginUpdater.java b/src/net/slipcor/pvparena/updater/PluginUpdater.java
index 515998886..9e8074a7a 100644
--- a/src/net/slipcor/pvparena/updater/PluginUpdater.java
+++ b/src/net/slipcor/pvparena/updater/PluginUpdater.java
@@ -45,6 +45,14 @@ protected void runUpdater() throws IOException {
         JsonObject versionJson = getVersionJson(connection.getInputStream());
         String onlineVersion = getOnlineVersionFromJson(versionJson);
 
+        // Detect 2.0
+        if(Long.parseLong(onlineVersion.split("\\.")[0]) > 1) {
+            String msg = "PVP Arena 2.0 has been released! Please visit our Spigot page to see how to update.";
+            LOG.info(msg);
+            this.updateMsgList.add(msg);
+            return;
+        }
+
         if(isUpToDate(currentVersion, onlineVersion)) {
             LOG.info("PVP Arena is up to date");
         } else {
diff --git a/src/plugin.yml b/src/plugin.yml
index a85f83219..aa6d1e796 100644
--- a/src/plugin.yml
+++ b/src/plugin.yml
@@ -1,245 +1,237 @@
 name: pvparena
 author: slipcor
-authors: [slipcor, Eredrim]
+authors: [ slipcor, Eredrim ]
 prefix: PVP Arena
 main: net.slipcor.pvparena.PVPArena
 version: ${project.version}${buildVersion}
 api-version: 1.13
-softdepend: [Spout,Multiverse-Core,MultiWorld,WormholeXTreme,Vault,WorldEdit,WorldGuard,LibsDisguises,DisguiseCraft,My Worlds,CrackShot]
+softdepend: [ Vault,WorldEdit,WorldGuard,LibsDisguises,DisguiseCraft,CrackShot,Multiverse-Core,MultiWorld,MyWorlds ]
 description: create, manage and enhance PvP arenas
-website: http://dev.bukkit.org/server-mods/pvparena
-dev-url: http://dev.bukkit.org/server-mods/pvparena
+website: https://www.spigotmc.org/resources/pvp-arena.16584
+dev-url: https://www.spigotmc.org/resources/pvp-arena.16584
 commands:
-    pvparena:
-        description: Base commands for the PvP arena.
-        permission: pvparena.user
-        aliases: ['pa']
-        usage: |
-            / help
-            / help admin | help administrating
-            / help setup | help setting up
-            / help game | help ingame
-            / help info | help getting information
+  pvparena:
+    description: Base commands for the PvP arena.
+    aliases: [ 'pa' ]
+    usage: |
+      / help
+      / help admin | help administrating
+      / help setup | help setting up
+      / help game | help ingame
+      / help info | help getting information
 permissions:
-    pvparena.*:
-        description: Gives access to everything
-        default: op
-        children:
-            pvparena.admin: true
-            pvparena.override: true
-            pvparena.telepass: true
-            pvparena.user: true
-            pvparena.cmds: true
-    pvparena.admin:
-        description: Allows you to administrate arenas
-        default: op
-        children:
-            pvparena.create: true
-    pvparena.cmds.*:
-        description: Allows you to do all commands
-        default: op
-        children:
-            pvparena.cmds.blacklist: true
-            pvparena.cmds.check: true
-            pvparena.cmds.class: true
-            pvparena.cmds.create: true
-            pvparena.cmds.debug: true
-            pvparena.cmds.disable: true
-            pvparena.cmds.duty: true
-            pvparena.cmds.edit: true
-            pvparena.cmds.enable: true
-            pvparena.cmds.gamemode: true
-            pvparena.cmds.goal: true
-            pvparena.cmds.install: true
-            pvparena.cmds.playerclass: true
-            pvparena.cmds.playerjoin: true
-            pvparena.cmds.protection: true
-            pvparena.cmds.region: true
-            pvparena.cmds.regionflag: true
-            pvparena.cmds.regions: true
-            pvparena.cmds.regiontype: true
-            pvparena.cmds.reload: true
-            pvparena.cmds.remove: true
-            pvparena.cmds.round: true
-            pvparena.cmds.set: true
-            pvparena.cmds.setowner: true
-            pvparena.cmds.setup: true
-            pvparena.cmds.spawn: true
-            pvparena.cmds.start: true
-            pvparena.cmds.stop: true
-            pvparena.cmds.teams: true
-            pvparena.cmds.teleport: true
-            pvparena.cmds.template: true
-            pvparena.cmds.togglemod: true
-            pvparena.cmds.uninstall: true
-            pvparena.cmds.update: true
-            pvparena.cmds.whitelist: true
-            pvparena.cmds.arenaclass: true
-            pvparena.cmds.chat: true
-            pvparena.cmds.join: true
-            pvparena.cmds.leave: true
-            pvparena.cmds.spectate: true
-            pvparena.cmds.arenalist: true
-            pvparena.cmds.help: true
-            pvparena.cmds.info: true
-            pvparena.cmds.list: true
-            pvparena.cmds.ready: true
-            pvparena.cmds.shutup: true
-            pvparena.cmds.stats: true
-            pvparena.cmds.version: true
-    pvparena.create:
-        description: Allows you to create and administrate an arena
-        default: op
-    pvparena.override:
-        description: Allows you to override some checks
-        default: op
-    pvparena.telepass:
-        description: Allows you to teleport while in an arena
-        default: op
-    pvparena.user:
-        description: Allows you to use the arena
-        default: true
-    pvparena.cmds.blacklist:
-        description: Allows you to run /pvparena blacklist
-        default: op
-    pvparena.cmds.check:
-        description: Allows you to run /pvparena check
-        default: op
-    pvparena.cmds.class:
-        description: Allows you to run /pvparena class
-        default: op
-    pvparena.cmds.create:
-        description: Allows you to run /pvparena create
-        default: op
-    pvparena.cmds.debug:
-        description: Allows you to run /pvparena debug
-        default: op
-    pvparena.cmds.disable:
-        description: Allows you to run /pvparena disable
-        default: op
-    pvparena.cmds.duty:
-        description: Allows you to run /pvparena duty
-        default: op
-    pvparena.cmds.edit:
-        description: Allows you to run /pvparena edit
-        default: op
-    pvparena.cmds.enable:
-        description: Allows you to run /pvparena enable
-        default: op
-    pvparena.cmds.gamemode:
-        description: Allows you to run /pvparena gamemode
-        default: op
-    pvparena.cmds.goal:
-        description: Allows you to run /pvparena goal
-        default: op
-    pvparena.cmds.install:
-        description: Allows you to run /pvparena install
-        default: op
-    pvparena.cmds.playerclass:
-        description: Allows you to run /pvparena playerclass
-        default: op
-    pvparena.cmds.playerjoin:
-        description: Allows you to run /pvparena playerjoin
-        default: op
-    pvparena.cmds.protection:
-        description: Allows you to run /pvparena protection
-        default: op
-    pvparena.cmds.region:
-        description: Allows you to run /pvparena region
-        default: op
-    pvparena.cmds.regionflag:
-        description: Allows you to run /pvparena regionflag
-        default: op
-    pvparena.cmds.regions:
-        description: Allows you to run /pvparena regions
-        default: op
-    pvparena.cmds.regiontype:
-        description: Allows you to run /pvparena regiontype
-        default: op
-    pvparena.cmds.reload:
-        description: Allows you to run /pvparena reload
-        default: op
-    pvparena.cmds.remove:
-        description: Allows you to run /pvparena remove
-        default: op
-    pvparena.cmds.round:
-        description: Allows you to run /pvparena round
-        default: op
-    pvparena.cmds.set:
-        description: Allows you to run /pvparena set
-        default: op
-    pvparena.cmds.setowner:
-        description: Allows you to run /pvparena setowner
-        default: op
-    pvparena.cmds.setup:
-        description: Allows you to run /pvparena setup
-        default: op
-    pvparena.cmds.spawn:
-        description: Allows you to run /pvparena spawn
-        default: op
-    pvparena.cmds.start:
-        description: Allows you to run /pvparena start
-        default: op
-    pvparena.cmds.stop:
-        description: Allows you to run /pvparena stop
-        default: op
-    pvparena.cmds.teams:
-        description: Allows you to run /pvparena teams
-        default: op
-    pvparena.cmds.teleport:
-        description: Allows you to run /pvparena teleport
-        default: op
-    pvparena.cmds.template:
-        description: Allows you to run /pvparena template
-        default: op
-    pvparena.cmds.togglemod:
-        description: Allows you to run /pvparena togglemod
-        default: op
-    pvparena.cmds.uninstall:
-        description: Allows you to run /pvparena uninstall
-        default: op
-    pvparena.cmds.update:
-        description: Allows you to run /pvparena update
-        default: op
-    pvparena.cmds.whitelist:
-        description: Allows you to run /pvparena whitelist
-        default: op
-    pvparena.cmds.arenaclass:
-        description: Allows you to run /pvparena arenaclass
-        default: true
-    pvparena.cmds.chat:
-        description: Allows you to run /pvparena chat
-        default: true
-    pvparena.cmds.join:
-        description: Allows you to run /pvparena join
-        default: true
-    pvparena.cmds.leave:
-        description: Allows you to run /pvparena leave
-        default: true
-    pvparena.cmds.spectate:
-        description: Allows you to run /pvparena spectate
-        default: true
-    pvparena.cmds.arenalist:
-        description: Allows you to run /pvparena arenalist
-        default: true
-    pvparena.cmds.help:
-        description: Allows you to run /pvparena help
-        default: true
-    pvparena.cmds.info:
-        description: Allows you to run /pvparena info
-        default: true
-    pvparena.cmds.list:
-        description: Allows you to run /pvparena list
-        default: true
-    pvparena.cmds.ready:
-        description: Allows you to run /pvparena ready
-        default: true
-    pvparena.cmds.shutup:
-        description: Allows you to run /pvparena shutup
-        default: true
-    pvparena.cmds.stats:
-        description: Allows you to run /pvparena stats
-        default: true
-    pvparena.cmds.version:
-        description: Allows you to run /pvparena version
-        default: true
+  pvparena.*:
+    description: Allows all
+    default: false
+    children:
+      pvparena.user: true
+      pvparena.admin: true
+  pvparena.user:
+    description: Allows you to use the arena
+    default: true
+    children:
+      pvparena.cmds.arenaclass: true
+      pvparena.cmds.chat: true
+      pvparena.cmds.join: true
+      pvparena.cmds.leave: true
+      pvparena.cmds.spectate: true
+      pvparena.cmds.arenalist: true
+      pvparena.cmds.help: true
+      pvparena.cmds.ready: true
+      pvparena.cmds.shutup: true
+      pvparena.cmds.stats: true
+  pvparena.admin:
+    description: Allows you to administrate arenas
+    default: op
+    children:
+      pvparena.cmds.*: true
+      pvparena.create: true
+      pvparena.telepass: true
+  pvparena.cmds.*:
+    description: Allows you to do all commands
+    default: false
+    children:
+      pvparena.cmds.blacklist: true
+      pvparena.cmds.check: true
+      pvparena.cmds.class: true
+      pvparena.cmds.create: true
+      pvparena.cmds.debug: true
+      pvparena.cmds.disable: true
+      pvparena.cmds.edit: true
+      pvparena.cmds.enable: true
+      pvparena.cmds.gamemode: true
+      pvparena.cmds.goal: true
+      pvparena.cmds.modules: true
+      pvparena.cmds.playerjoin: true
+      pvparena.cmds.protection: true
+      pvparena.cmds.region: true
+      pvparena.cmds.regionflag: true
+      pvparena.cmds.regions: true
+      pvparena.cmds.regiontype: true
+      pvparena.cmds.reload: true
+      pvparena.cmds.remove: true
+      pvparena.cmds.set: true
+      pvparena.cmds.spawn: true
+      pvparena.cmds.start: true
+      pvparena.cmds.stop: true
+      pvparena.cmds.teams: true
+      pvparena.cmds.teleport: true
+      pvparena.cmds.template: true
+      pvparena.cmds.togglemod: true
+      pvparena.cmds.whitelist: true
+      pvparena.cmds.info: true
+      pvparena.cmds.list: true
+      pvparena.cmds.version: true
+  pvparena.create:
+    description: Allows you to create and administrate an arena
+    default: op
+  pvparena.override:
+    description: Allows you to override some checks
+    default: op
+  pvparena.telepass:
+    description: Allows you to teleport while in an arena
+    default: op
+  pvparena.cmds.blacklist:
+    description: Allows you to run /pvparena blacklist
+    default: op
+  pvparena.cmds.check:
+    description: Allows you to run /pvparena check
+    default: op
+  pvparena.cmds.class:
+    description: Allows you to run /pvparena class
+    default: op
+  pvparena.cmds.create:
+    description: Allows you to run /pvparena create
+    default: op
+  pvparena.cmds.debug:
+    description: Allows you to run /pvparena debug
+    default: op
+  pvparena.cmds.disable:
+    description: Allows you to run /pvparena disable
+    default: op
+  pvparena.cmds.duty:
+    description: Allows you to run /pvparena duty
+    default: op
+  pvparena.cmds.edit:
+    description: Allows you to run /pvparena edit
+    default: op
+  pvparena.cmds.enable:
+    description: Allows you to run /pvparena enable
+    default: op
+  pvparena.cmds.gamemode:
+    description: Allows you to run /pvparena gamemode
+    default: op
+  pvparena.cmds.goal:
+    description: Allows you to run /pvparena goal
+    default: op
+  pvparena.cmds.install:
+    description: Allows you to run /pvparena install
+    default: op
+  pvparena.cmds.playerclass:
+    description: Allows you to run /pvparena playerclass
+    default: op
+  pvparena.cmds.playerjoin:
+    description: Allows you to run /pvparena playerjoin
+    default: op
+  pvparena.cmds.protection:
+    description: Allows you to run /pvparena protection
+    default: op
+  pvparena.cmds.region:
+    description: Allows you to run /pvparena region
+    default: op
+  pvparena.cmds.regionflag:
+    description: Allows you to run /pvparena regionflag
+    default: op
+  pvparena.cmds.regions:
+    description: Allows you to run /pvparena regions
+    default: op
+  pvparena.cmds.regiontype:
+    description: Allows you to run /pvparena regiontype
+    default: op
+  pvparena.cmds.reload:
+    description: Allows you to run /pvparena reload
+    default: op
+  pvparena.cmds.remove:
+    description: Allows you to run /pvparena remove
+    default: op
+  pvparena.cmds.round:
+    description: Allows you to run /pvparena round
+    default: op
+  pvparena.cmds.set:
+    description: Allows you to run /pvparena set
+    default: op
+  pvparena.cmds.setowner:
+    description: Allows you to run /pvparena setowner
+    default: op
+  pvparena.cmds.setup:
+    description: Allows you to run /pvparena setup
+    default: op
+  pvparena.cmds.spawn:
+    description: Allows you to run /pvparena spawn
+    default: op
+  pvparena.cmds.start:
+    description: Allows you to run /pvparena start
+    default: op
+  pvparena.cmds.stop:
+    description: Allows you to run /pvparena stop
+    default: op
+  pvparena.cmds.teams:
+    description: Allows you to run /pvparena teams
+    default: op
+  pvparena.cmds.teleport:
+    description: Allows you to run /pvparena teleport
+    default: op
+  pvparena.cmds.template:
+    description: Allows you to run /pvparena template
+    default: op
+  pvparena.cmds.togglemod:
+    description: Allows you to run /pvparena togglemod
+    default: op
+  pvparena.cmds.uninstall:
+    description: Allows you to run /pvparena uninstall
+    default: op
+  pvparena.cmds.update:
+    description: Allows you to run /pvparena update
+    default: op
+  pvparena.cmds.whitelist:
+    description: Allows you to run /pvparena whitelist
+    default: op
+  pvparena.cmds.arenaclass:
+    description: Allows you to run /pvparena arenaclass
+    default: true
+  pvparena.cmds.chat:
+    description: Allows you to run /pvparena chat
+    default: true
+  pvparena.cmds.join:
+    description: Allows you to run /pvparena join
+    default: true
+  pvparena.cmds.leave:
+    description: Allows you to run /pvparena leave
+    default: true
+  pvparena.cmds.spectate:
+    description: Allows you to run /pvparena spectate
+    default: true
+  pvparena.cmds.arenalist:
+    description: Allows you to run /pvparena arenalist
+    default: true
+  pvparena.cmds.help:
+    description: Allows you to run /pvparena help
+    default: true
+  pvparena.cmds.info:
+    description: Allows you to run /pvparena info
+    default: true
+  pvparena.cmds.list:
+    description: Allows you to run /pvparena list
+    default: true
+  pvparena.cmds.ready:
+    description: Allows you to run /pvparena ready
+    default: true
+  pvparena.cmds.shutup:
+    description: Allows you to run /pvparena shutup
+    default: true
+  pvparena.cmds.stats:
+    description: Allows you to run /pvparena stats
+    default: true
+  pvparena.cmds.version:
+    description: Allows you to run /pvparena version
+    default: true