diff --git a/.gitignore b/.gitignore index 96ef862..90b6399 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ target/ .idea/ +*.secret +*.db +nbactions.xml +stargate.iml diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 2e16cba..0000000 --- a/Jenkinsfile +++ /dev/null @@ -1,33 +0,0 @@ -pipeline { - agent any - tools { - jdk 'JDK17' - } - stages { - stage('Build') { - steps { - echo 'Building...' - sh 'mvn clean & mvn validate & mvn compile' - } - } - stage('Test') { - steps { - echo 'Testing...' - sh 'mvn test' - } - } - stage('Verify') { - steps { - echo 'Verifying...' - sh 'mvn verify -Dmaven.test.skip=true' - } - } - stage('Deploy') { - steps { - echo 'Deploying...' - sh 'mvn deploy -Dmaven.install.skip=true -Dmaven.test.skip=true' - archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true - } - } - } -} \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 3d90694..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - 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 -. \ No newline at end of file diff --git a/README.md b/README.md index 5d4f412..d6777e9 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,23 @@ +> **Documentation may be found** [here](https://sgrewritten.org/legacywiki)
+> **Support is available via** [discord](https://sgrewritten.org/discord)**.** + +> **THIS IS A LEGACY-BASED BRANCH: IT IS SUPPORTED, BUT NOT ACTIVELY UPDATED**
+> This branch expands upon Drakia's original 2013 codebase, with fixes as needed.
+> In the near future, this branch will be superseded by [SGR](https://github.com/stargate-rewritten/Stargate-Bukkit) (a +> complete rewrite). + # Description -Create gates that allow for instant-teleportation between large distances. Gates can be always-open or triggered; they -can share a network or be split into clusters; they can be hidden on a network or accessible to everybody. +The Original, and still the best, MineCraft transportation solution!
Intuitively and organically facilitates instant +transportation across large distances!

+Highly capable, simple to use, with robust network capabilities and extensive customisability!
- **Player permissions** -- let players build their own networks. - **Vault economy support** -- can add costs for create, destroy and use. -- **Ability to create custom gate configurations**. Four different default gate configurations are available. +- **Ability to create custom gate configurations**. -- Four different default gate configurations are available. +- **Ability to include #Tags in gate designs**. (Ability to include material lists as valid options within a format) - **Message customization** -- **Multiple built-in languages** (de, en, es, fr, hu, it, ja, nb-no, nl, nn-no, pt-br, ru, zh_cn) +- **Multiple built-in languages** (cs, de, en, es, fr, hu, it, ja, nb, nl, nn, pt, ru, uk, zh) - **Teleport across worlds or servers** (BungeeCord supported) - **Vehicle teleportation** -- teleport minecarts, boats, horses, pigs and striders - **Leashed teleportation** -- teleport any creature in a leash with the player @@ -18,28 +28,42 @@ can share a network or be split into clusters; they can be hidden on a network o - **Color customization** -- Stargate signs can be colored in many ways. Colors can be set globally, or on a per sign type basis - **RGB and dye support** -- Signs can use RGB colors (using hex codes) as their main and highlighting colors, and can - also be dyed on a per-sign basis + also be dyed on a per-sign basis. ## Background -This was originally TheDgtl's Bukkit port of the Stargate plugin for hMod by Dinnerbone. This is a fork -of [PseudoKnight's fork](https://github.com/PseudoKnight/Stargate-Bukkit). This fork's main purpose is to create a clean -version of Stargate compliant with Spigot 1.17, even if it means changing the entire project's previous structure. +- This plugin was originally TheDgtl's Bukkit port of the Stargate plugin for hMod by Dinnerbone. +- After this plugin was dropped by TheDgtl, PseudoKnight began maintaining it for modern versions of Spigot (adding + support for UUIDs & Material Strings). +- EpicKnarvik97 forked that version to clean up the code, added leash support, and improved vehicle support. +- LockedCraft and LittleBigBug also forked that version to add underwater and tag support, as well as a few bug fixes. +- This version is a combination of all the forks above, maintained by the Stargate Rewritten project. +- This branch is currently in a maintenance-only mode; a total rewrite is forthcoming. ## License -Stargate is licensed under the GNU Lesser General Public License Version 3.0. This includes every source and resource -file. See the HEADER file for a more detailed license description. +Stargate is licensed under the GNU Lesser General Public License Version 3.0.
This includes every source and +resource +file; see the LICENSE file for more information. ## Migration -This plugin should be compatible with configurations from the Stargate plugin all the way back. The nethergate.gate -file, the endgate.gate file and the watergate.gate file will be overwritten if they exist. Take a backup of the files -and overwrite the files after the first startup if you want to keep your custom gates. +This plugin should be fully compatible all known versions StarGate forks, with the following exceptions:
-If you have legacy gate files using the old numeric material ids, you need to change them to the new format manually. -Use F3 + H to see material ids. Use them exactly as written after "minecraft:". The configuration will be updated to a -more easily readable format, but the old configuration will be saved in case you want to change back right away. +- Any version from outside the bukkit ecosystem +- Any version of SGR (version numbers 1.0.0.0+) +- Any configurations with outdated material names (i.e. numIDs) + +**Note that this plugin's default gate files** __**AND ANY PRESENT CUSTOM .GATEs**__ **will be overwritten by the +import!**
+If you wish to keep any such files, take a backup of your "gates" folder! + +Legacy gate files filled with outdated material IDs will need to be manually updated.
+A list of old materials and their conversions may be +found [here](https://github.com/CryptoMorin/XSeries/blob/master/src/main/java/com/cryptomorin/xseries/XMaterial.java) +.
+A list of modern, valid, material names may be +found [here](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html).
Permissions have had a few changes, so you should check the permissions section for any differences since you set up permissions. @@ -50,77 +74,85 @@ server. # Permissions -``` -stargate.use -- Allow use of all Stargates linking to any world in any network (Override ALL network/world permissions. Set to false to use network/world specific permissions) - stargate.world -- Allow use of Stargates linking to any world - stargate.world.{world} -- Allow use of Stargates with a destination in {world}. Set to false to disallow use. - stargate.network -- Allow use of Stargates on all networks - stargate.network.{network} -- Allow use of all Stargates in {network}. Set to false to disallow use. - stargate.server -- Allow use of Stargates going to all servers - stargate.server.{server} -- Allow usee of all Stargates going to {server}. Set to false to disallow use. - -stargate.option -- Allow use of all options - stargate.option.hidden -- Allow use of 'H'idden - stargate.option.alwayson -- Allow use of 'A'lways-On - stargate.option.private -- Allow use of 'P'rivate - stargate.option.free -- Allow use of 'F'ree - stargate.option.backwards -- Allow use of 'B'ackwards - stargate.option.show -- Allow use of 'S'how - stargate.option.nonetwork -- Allow use of 'N'oNetwork - stargate.option.random -- Allow use of 'R'andom stargates - stargate.option.silent -- Allow use of S'i'lent stargates - stargate.option.nosign -- Allow use of 'E' (No sign) - -stargate.create -- Allow creating Stargates on any network (Override all create permissions) - stargate.create.personal -- Allow creating Stargates on network {playername} - stargate.create.network -- Allow creating Stargates on any network - stargate.create.network.{networkname} -- Allow creating Stargates on network {networkname}. Set to false to disallow creation on {networkname} - stargate.create.gate -- Allow creation using any gate layout - stargate.create.gate.{gatefile} -- Allow creation using only {gatefile} gates +## Easy setup permission groups -stargate.destroy -- Allow destruction of Stargates on any network (Orderride all destroy permissions) - stargate.destroy.personal -- Allow destruction of Stargates owned by the player only - stargate.destroy.network -- Allow destruction of Stargates on any network - stargate.destroy.network.{networkname} -- Allow destruction of Stargates on network {networkname}. Set to false to disallow destruction of {networkname} +Note: These permission groups inherit from each other (builder has player's permissions and admin has builder's +permissions), so you only need the most permissive one. -stargate.free -- Allow free use/creation/destruction of Stargates - stargate.free.use -- Allow free use of Stargates - stargate.free.create -- Allow free creation of Stargates - stargate.free.destroy -- Allow free destruction of Stargates - -stargate.admin -- Allow all admin features (Hidden/Private bypass, BungeeCord, Reload, Config) - stargate.admin.private -- Allow use of Private gates not owned by user - stargate.admin.hidden -- Allow access to Hidden gates not ownerd by user - stargate.admin.bungee -- Allow the creation of BungeeCord stargates (U option) - stargate.admin.reload -- Allow use of the reload command - stargate.admin.config -- Allows the player to change config values from the chat - stargate.admin.dye -- Allows this player to change the dye of any stargate's sign -``` +| Node | Default | Description | +|------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| stargate.group.player | True | Allows using all non-private/non-personal Stargates and creating personal Stargates using the default nethergate format. | +| stargate.group.builder | False | Allows creating and destroying all types of Stargates anywhere with any option (except BungeeCord). | +| stargate.group.admin | Op | Allows admin stuff like creating BungeeCord gates, reloading, changing the configuration, dying any Stargate's sign and accessing private and hidden Stargates. | +| stargate.* | False | Gives all Stargate-related permissions | -## Default Permissions +## Individual nodes -``` -stargate.use -- Everyone -stargate.create -- Op -stargate.destroy -- Op -stargate.option -- Op -stargate.free -- Op -stargate.admin -- Op -``` +
+ The full list of permission nodes may be found below: (Click to expand) + +Note: `-` is used to show inheritance, such as `stargate.use` being a parent of `stargate.world`, `stargate.network`, +and `stargate.server`, thus giving all permissions of the indented items following the unindented node. + +| Node | Description | +|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| stargate.use | Allow use of all Stargates linking to any world in any network (Override ALL network/world permissions. Set to false to use network/world specific permissions) | +| - stargate.world | Allow use of Stargates linking to any world | +| -- stargate.world.{world} | Allow use of Stargates with a destination in {world}. Set to false to disallow use. | +| - stargate.network | Allow use of Stargates on all networks | +| -- stargate.network.{network} | Allow use of all Stargates in {network}. Set to false to disallow use. | +| - stargate.server | Allow use of Stargates going to all servers | +| -- stargate.server.{server} | Allow usee of all Stargates going to {server}. Set to false to disallow use. | +| | | +| stargate.option | Allow use of all options | +| - stargate.option.hidden | Allow use of 'H'idden | +| - stargate.option.alwayson | Allow use of 'A'lways-On | +| - stargate.option.private | Allow use of 'P'rivate | +| - stargate.option.free | Allow use of 'F'ree | +| - stargate.option.backwards | Allow use of 'B'ackwards | +| - stargate.option.show | Allow use of 'S'how | +| - stargate.option.nonetwork | Allow use of 'N'oNetwork | +| - stargate.option.random | Allow use of 'R'andom stargates | +| - stargate.option.quiet | Allow use of 'Q'uiet stargates | +| - stargate.option.nosign | Allow use of 'V' (No sign) | +| | | +| stargate.create | Allow creating Stargates on any network (Override all create permissions) | +| - stargate.create.personal | Allow creating Stargates on network {playername} | +| - stargate.create.network | Allow creating Stargates on any network | +| -- stargate.create.network.{networkname} | Allow creating Stargates on network {networkname}. Set to false to disallow creation on {networkname} | +| - stargate.create.gate | Allow creation using any gate layout | +| -- stargate.create.gate.{gatefile} | Allow creation using only {gatefile} gates | +| | | +| stargate.destroy | Allow destruction of Stargates on any network (Overrides all destroy permissions) | +| - stargate.destroy.personal | Allow destruction of Stargates owned by the player only | +| - stargate.destroy.network | Allow destruction of Stargates on any network | +| -- stargate.destroy.network.{networkname} | Allow destruction of Stargates on network {networkname}. Set to false to disallow destruction of {networkname} | +| | | +| stargate.free | Allow free use/creation/destruction of Stargates | +| - stargate.free.use | Allow free use of Stargates | +| - stargate.free.create | Allow free creation of Stargates | +| - stargate.free.destroy | Allow free destruction of Stargates | +| | | +| stargate.admin | Allow all admin features (Hidden/Private bypass, BungeeCord, Reload, Config) | +| - stargate.admin.private | Allow use of Private gates not owned by user | +| - stargate.admin.hidden | Allow access to Hidden gates not owned by the user | +| - stargate.admin.bungee | Allow the creation of BungeeCord stargates (U option) | +| - stargate.admin.reload | Allow use of the reload command | +| - stargate.admin.config | Allows the player to change config values from the chat | +| - stargate.admin.dye | Allows this player to change the dye of any stargate's sign | + +
# Instructions ## Building a gate: -There are currently three default gate configurations. They all use the same structure as a standard nether portal. One -gate is using obsidian blocks, one is using end bricks and the last uses sea lanterns. Only the sea lantern one can be -used underwater. You must put a sign on one of the blocks in the middle of the layout to activate the portal (see next -section). See the Custom Gate Layout section to learn how to add custom gates. +This a default gate configuration. See the Custom Gate Layout section for more options, and how to redesign this. ``` OO - O O - These are Obsidian blocks, End bricks or Sea Lanterns. You need 10. - O O - Place a sign on either of these two middle blocks. + O O - These are Obsidian blocks (End Bricks and Sea Lanterns also work). You need 10. + ■ ■ - Place a sign on either of these two blocks. O O OO ``` @@ -128,25 +160,23 @@ section). See the Custom Gate Layout section to learn how to add custom gates. ### Sign Layout: - Line 1: Gate Name (Max 13 characters) -- Line 2: Destination Name \[Optional] (Max 13 characters, used for fixed-gates only) -- Line 3: Network name \[Optional] (Max 13 characters) -- Line 4: Options \[Optional] : - - 'A' for always-on fixed gate - - 'H' for hidden networked gate - - 'P' for a private gate - - 'F' for a free gate - - 'B' is for a backwards facing gate (You will exit the back) - - 'S' is for showing an always-on gate in the network list - - 'N' is for hiding the network name - - 'R' is for random gates. These follow standard permissions of gates, but have a random exit location every time a - player enters. (Implicitly always on) - - 'U' is for a gate connecting to another through bungee (Implicitly always on) - - 'I' is for a silent gate, which does not output anything to the chat while teleporting. Increases immersion - - 'E' is for a gate without a sign. Only for fixed stargates +- Line 2: Destination Name [Optional] (Max 13 characters, used for fixed-gates only) +- Line 3: Network name [Optional] (Max 13 characters) +- Line 4: Options [Optional] : + - `A` is for an **A**lways-on fixed gate + - `H` is for a **H**idden networked gate + - `P` is for a **P**rivate gate + - `F` is for a **F**ree gate + - `B` is for a **B**ackwards facing gate (which exit you at the back) + - `S` is for **S**howing an always-on gate in the network list + - `N` is for a **N**o network gate (the network name is hidden from the sign) + - `R` is for a **R**andom gate (implicitly always on; sends players to a random exit) + - `U` is for a b**U**ngee gate (connecting to another servers via bungeecord) + - `Q` is for a **Q**uiet gate (it will not output anything to chat when teleporting) + - `V` is for an in**V**isible gate (it will appear without a sign) -The options are the single letter, not the word. So to make a private hidden gate, your 4th line would be 'PH'. The -&\[0-9a-f] color codes are not counted in the character limit, thus allowing a 13-character name with an additional 2 -characters used for the color code. +The options are the single letter, not the word. So to make a private hidden gate, your 4th line would be 'PH'.
+Note that colour characters (if enabled) are not counted towards the character limit. #### Gate networks: @@ -154,7 +184,7 @@ characters used for the color code. - You can specify (and create) your own network on the third line of the sign when making a new gate. - Gates on one network will not see gates on the second network, and vice versa. - Gates on different worlds, but in the same network, will see each other. -- If the gate is a bungee gate, the network name should be the name of the server as displayed when typing /servers +- Notwithstanding the above, the network for BungeeCord gates will always be the name of its destination /server #### Fixed gates: @@ -162,26 +192,39 @@ characters used for the color code. - Fixed gates can be linked to other fixed gates, or normal gates. A normal gate cannot open a portal to a fixed gate, however. - To create a fixed gate, specify a destination on the second line of the stargate sign. -- Set the 4th line of the stargate sign to "A" to enable an always-open fixed gate. -- A bungee gate is always automatically a fixed gate +- Set the 4th line of the stargate sign to `A` to enable an always-open fixed gate. +- Gates with the U or R flags are fixed gates by definition. #### Hidden Gates: - Hidden gates are like normal gates, but only show on the destination list of other gates under certain conditions. -- A hidden gate is only visible to the creator of the gate, or somebody with the stargate.hidden permission. -- Set the 4th line of the stargate sign to 'H' to make it a hidden gate. +- A hidden gate is only visible to the creator of the gate or somebody with the stargate.hidden permission. +- Set the 4th line of the stargate sign to `H` to make it a hidden gate. + +#### Force Shown Gates: + +- Gates with the `A`, `R`, or `U` gates do not show up on networks by default. +- To force such gates to show up on network lists, add the `S` flag to the sign's 4th line. + +#### Random Gates: + +- Random gates are similar to Always-On gates, but do not have a fixed exit; + - They instead randomly select an exit from the list of gates on their network. +- Marking a gate as 'R' will automatically make that gate always-on. +- 'R' gates ignore any gate with the 'R', 'A', and/or 'S' flag(s) when choosing their exit. ## Using a gate: -- Right-click the sign to choose a destination. -- Right-click the button to open up a portal. +- Right-click the sign to choose a destination (not needed for Fixed gates, undefined gates). +- Right-click the activator to open up a portal. - Step through. -# Custom Gate Layout +## Custom Gate Layouts -You can create as many gate formats as you want, the gate layouts are stored in `plugins/Stargate/gates/`. -The .gate file must be laid out a specific way, the first lines will be config information, and after a blank line you -will lay out the gate format. Here is the default nethergate.gate file: +You may create as many gate formats as you wish through the use of .gate files within your `gates` folder.
+ +The .gate file follows a specific format, with config lines at the top, and the gate layout/design below it.
+For example, take the default nether.gate file shown below: ``` portal-open=NETHER_PORTAL @@ -193,53 +236,56 @@ destroycost=0 toowner=false X=OBSIDIAN -=OBSIDIAN - - XX + XX X..X -..- X*.X XX ``` -The keys `portal-open` and `portal-closed` are used to define the material in the gate when it is open or closed. The -material for `portal-closed` can be most things, including solid blocks. Some materials may act weirdly though. The -material for `portal-open` can be any block the player can partially enter, even things like `GLOW_LICHEN`. -`NETHER_PORTAL`, `END_GATEWAY` and `END_PORTAL` all work. +### Keys -The `usecost`, `createcost` and `destroycost` keys can be used to set an economy price for gates of this type, different -from the cost defined in the config. With economy enabled, all gates without these values set will use the values from -the config. If you want to have different costs for different portals, you must create different gate types and set -different costs for each one. The `toowner` key can be used to set whether funds withdrawn for using portals with this -gate type should go to the portal's owner. +#### Materials -The key `button` is used to define the type of button that is generated for this gate. It can be a button (of any type), -a type of wall coral (dead or alive), a type of shulker box or a chest. +> Note that MATERIAL NAMES (such as `OBSIDIAN`) can be +> found [here](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html).
+> As of version 0.10.7.0, TAGS (such as `#WOOL`) can be +> found [here](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Tag.html). -`X` and `-` are used to define block types for the layout (Any single-character can be used, such as `#`). -In the gate format, you can see we use `X` to show where obsidian must be, `-` where the controls (Button/sign) are. +- `portal-open` and `portal-closed` are used to specify + the [materials](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html) the portal's iris will use when + active/inactive respectively. + - Note that portal-open should be a *traversable* material, such as those + listed [here](https://sgrewritten.org/traversables). +- Notwithstanding `#`, any single character may be used to represent any material (for example, `X` as a representation + of `OBSIDIAN`). +- `-` is a special character: it represents the material to be used for behind your portal's control blocks (activator + + sign). +- `button` is used to define what is used for the gate's *activator*. It may be any type of button, wall coral, or + container. +- All materials can use a single tag or material, or a comma-separated list of tags, materials or both (#WOOL,Obsidian). + Upon generating a button, or opening/closing a Stargate, a random possible material is chosen. When validating, any + material/tag specified in the comma-separated list is considered valid. -For more complex gate designs, it is possible to add more materials. If you add something like a=GLOWSTONE, `a` can then -be used in the gate layout, just as `X` is used. See the `squarenetherglowstonegate.gate` file for an example. - -You will also notice a `*` in the gate layout, this is the "exit point" of the gate, the block at which the player will -teleport in front of. - -## Buttons - -The actual buttons cannot be used underwater, but all the other items in the button list can be.
- The entire list of button types is as follows: (Click to expand) + The full list of valid activator types may be found below: (Click to expand) ``` -STONE_BUTTON +Any kind of button (automatically updated): + OAK_BUTTON SPRUCE_BUTTON BIRCH_BUTTON JUNGLE_BUTTON ACACIA_BUTTON DARK_OAK_BUTTON +MANGROVE_BUTTON +CHERRY_BUTTON +PALE_OAK_BUTTON +BAMBOO_BUTTON CRIMSON_BUTTON WARPED_BUTTON +STONE_BUTTON POLISHED_BLACKSTONE_BUTTON CHEST @@ -276,339 +322,498 @@ DEAD_HORN_CORAL_WALL_FAN
-## Underwater Portals +#### Economy -There is a default gate type for underwater gates. There are no real restrictions on underwater gate materials, except -normal buttons cannot be used since they'd fall off. Using wall coral fans work much better, though `CHEST` and -`SHULKER_BOX` works too. +> These values require `useEconomy` to be true. -Using `AIR` for a closed underwater gate looks weird, so `WATER` might be better. If using `AIR` for the closed gate, -you need to make sure it actually contains air when creating it. For partially submerged portals, like ones used for -boat teleportation, you need to keep water away from the portal entrance/opening until it's been created. +- `useCost` defines the cost players are charged when using an existing portal. +- `createCost` defines the cost players are charged when constructing a new portal. +- `destroyCost` defines the cost players are charged when breaking an existing portal. +- `toOwner` specifies the money's destination: if true, it will go to the player who made (owns) the gate; if false, the + money will be deleted. -## Economy Support: +### Structure -The latest version of Stargate has support for Vault. Gate creation, destruction and use can all have different costs -associated with them. You can also define per-gate layout costs. The default cost is assigned in the config.yml file, -while the per-gate costs re defined in the .gate files. To define a certain cost to a gate just add these lines to your -.gate file: +#### Standard Custom Gates + +##### Basic formatting: + +Following a blank line after your keys section, you may specify your gate layout:
Simply lay out the portal as it +will appear in your world, with every character representing a block. ``` - createCost: 5 -- Will cost 5 currency to create - destroyCost: 5 -- Will clost 5 currency to destroy (negative to get back the spent money) - useCost: 5 -- Will cost 5 currency to use the stargate - toOwner: true -- Will send any fees to the gate's owner +X=OBSIDIAN +-=OBSIDIAN + XX +X..X +-..- +X*.X + XX +``` + +This example is a standard nether portal. + +Any single character symbol (except for `#`) may represent any material
+In this case, `X` and `-` point to OBSIDIAN.
+
`-` is a special material character that specifies where the control blocks will be placed (i.e. sign & button) +.
In this case, it is also `OBSIDIAN`. + +Other special characters include the following: + +- Spaces/blank characters (` `) will not be checked, and as such, represent any block. +- Periods (`.`) represent the portal's *iris* (i.e. the part that opens and closes) +- An asterix (`*`) represents the portals' "exit point" (i.e. the block the player will teleport in front of). + +##### Underwater Portals + +Gates may be constructed underwater in much the same manner as they may be constructed above the surface.
+There are, however, a few considerations for underwater portals: + +``` +portal-open=KELP_PLANT +portal-closed=WATER +button=BRAIN_CORAL_WALL_FAN +toowner=false +X=SEA_LANTERN +-=SEA_LANTERN + XX +X..X +-..- +X*.X + XX +``` + +- Buttons can not be waterlogged, and as such, are not ideal: wall coral fans are an ideal substitute. + - Containers (such as `CHEST` and `SHULKER_BOX`) are also valid alternatives. +- `AIR` is generally a poor `portal-closed` material for underwater portals, since such portals are difficult to + construct and are visually problematic. + - `WATER` and other underwater [traversables](https://sgrewritten.org/traversables) work much better. + +##### Advanced format + +Gates are not limited to the shape of a standard nether portal -- they can be thousands of blocks big!
+In this case, a simple 5x5 square has been used as a gate. + +Gates are also not limited to [materials](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html) (such +as `OBSIDIAN`); they may also use [tags](https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Tag.html) (such +as `#WOOL`), or a comma-separated list (such as `OBSIDIAN,#WOOL`).
+Note that all tags must be prefaced with a hashtag (`#`), as in `#WOOL`. + +``` +portal-open=WATER +portal-closed=AIR +X=#WOOL +-=#WOOL +XXXXX +X...X +-...- +X.*.X +XXXXX +``` + +Any block that is included within a given tag may be used to construct the portal. + +Furthermore, it is important to note that gates may have multiple materials in their frame, as shown below: + +``` +portal-open=NETHER_PORTAL +portal-closed=AIR +button=OAK_BUTTON +toowner=false +X=OBSIDIAN +-=GLOWSTONE +A=GLOWSTONE + XAX +X...X +-...- +X.*.X + XAX ``` # Configuration -``` -language - The language to use (Included languages: en, de, es, fr, hu, it, ja, nb-no, nl, nn-no, pt-br, ru, zh_cn) -adminUpdateAlert - Whether to alert admins about an available update when joining the server -folders: - portalFolder - The folder your portal databases are saved in - gateFolder - The folder containing your .gate files -gates: - maxGatesEachNetwork - If non-zero, will define the maximum amount of gates allowed on any network. - defaultGateNetwork - The default gate network - exitVelocity - The velocity to give players exiting stargates, relative to the entry velocity (1 = same as entry velocity) - cosmetic: - rememberDestination - Whether to set the first destination as the last used destination for all gates - sortNetworkDestinations - If true, network lists will be sorted alphabetically. - mainSignColor - This allows you to specify the color of the gate signs. Use a color code such as WHITE,BLACK,YELLOW or a hex color code such as '#ed76d9'. You need quotes around hex color codes. - highlightSignColor - This allows you to specify the color of the sign markings. Use a color code such as WHITE,BLACK,YELLOW or a hex color code such as '#ed76d9'. You need quotes around hex color codes. - perSignColors: - A list of per-sign color specifications. Format: "SIGN_TYPE:mainColor,highlight_color". The SIGN_TYPE is OAK for an oak sign, DARK_OAK for a dark oak sign and so on. The colors can be "default" to use the color specified in "mainSignColor" or "highlightSignColor", "inverted" to use the inverse color of the default color, a normal color such as BLACK,WHITE,YELLOW or a hex color code such as #ed76d9. - integrity: - destroyedByExplosion - Whether to destroy a stargate with explosions, or stop an explosion if it contains a gates controls. - verifyPortals - Whether or not all the non-sign blocks are checked to match the gate layout when an old stargate is loaded at startup. - protectEntrance - If true, will protect from users breaking gate entrance blocks (This is more resource intensive than the usual check, and should only be enabled for servers that use solid open/close blocks) - functionality: - enableBungee - Enable this for BungeeCord support. This allows portals across Bungee servers. - handleVehicles - Whether or not to handle vehicles going through gates. Set to false to disallow vehicles (Manned or not) going through gates. - handleEmptyVehicles - Whether or not to handle empty vehicles going through gates (chest/hopper/tnt/furnace minecarts included). - handleCreatureTransportation - Whether or not to handle players that transport creatures by sending vehicles (minecarts, boats) through gates. - handleNonPlayerVehicles - Whether or not to handle vehicles with a passenger which is not a player going through gates (pigs, horses, villagers, creepers, etc.). handleCreatureTransportation must be enabled. - handleLeashedCreatures - Whether or not to handle creatures leashed by a player going through gates. Set to false to disallow leashed creatures going through gates. - enableCraftBookRemoveOnEjectFix - Whether to enable a fix that causes loss of NBT data, but allows vehicle teleportation to work when CraftBook's remove minecart/boat on eject setting is enabled -economy: - useEconomy - Whether or not to enable Economy using Vault (must have the Vault plugin) - createCost - The cost to create a stargate - destroyCost - The cost to destroy a stargate (Can be negative for a "refund" - useCost - The cost to use a stargate - toOwner - Whether the money from gate-use goes to the owner or nobody - chargeFreeDestination - Enable to make players pay for teleportation even if the destination is free - freeGatesColored - Enable to make gates that won't cost the player money show up as green - freeGatesColor - This allows you to specify the color of the markings and name of free stargates -debugging: - debug - Whether to show massive debug output - permissionDebug - Whether to show massive permission debug output -advanced: - waitForPlayerAfterTeleportDelay - The amount of ticks to wait before adding a player as passenger of a vehicle. On slow servers, a value of 6 is required to avoid client glitches after teleporting on a vehicle. -``` +| Node | Default | Description | +|----------------------------------|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| language | en | The language to use (Included languages: cs, de, en, es, fr, hu, it, ja, nb, nl, nn, pt, ru, uk, zh). | +| adminUpdateAlert | true | Whether to alert admins about an available update when joining the server. | +| dynmap | | | +| .enableDynmap | true | Should StarGate enable integration with Dynmap? This will show StarGates on your map. | +| .dynmapIconsHiddenByDefault | false | Should StarGate icons be hidden by default? (must users manually enable their checkbox?) | +| gates | | | +| .maxGatesEachNetwork | 0 | If non-zero, will define the maximum amount of gates allowed on any network. | +| .defaultGateNetwork | central | The default network for any non-personal Stargate without an explicitly defined network. | +| .exitVelocity | 0.1 | The velocity to give players exiting stargates, relative to the entry velocity (1 = same as entry velocity) | +| gates.functionality | | | +| .enableCraftBookRemoveOnEjectFix | false | Should the plugin sacrifice vehicle integrity in order to fix a CraftBook destroy on exit incompatibility? | +| .enableBungee | false | Enable this for BungeeCord support. This allows portals across Bungee servers. | +| .handleVehicles | true | Whether or not to handle vehicles going through gates. Set to false to disallow vehicles (Manned or not) going through gates. | +| .handleEmptyVehicles | true | Whether or not to handle empty vehicles going through gates (chest/hopper/tnt/furnace minecarts included). | +| .handleCreatureTransportation | true | Whether to handle players that transport creatures by sending vehicles (minecarts, boats) through gates. | +| .handleNonPlayerVehicles | true | Whether or not to handle vehicles with a passenger which is not a player going through gates (pigs, horses, villagers, creepers, etc.). handleCreatureTransportation must be enabled. | +| .handleLeashedCreatures | true | Whether or not to handle creatures leashed by a player going through gates. Set to false to disallow leashed creatures going through gates. | +| gates.integrity | | | +| .destroyedByExplosion | false | Whether to destroy a stargate with explosions, or stop an explosion if it contains a gates controls. | +| .verifyPortals | false | Whether or not all the non-sign blocks are checked to match the gate layout when an old stargate is loaded at startup. | +| .protectEntrance | false | If true, will protect from users breaking gate entrance blocks (This is more resource intensive than the usual check, and should only be enabled for servers that use solid open/close blocks) | +| .controlUpdateDelay | 3 | The amount of ticks to wait between each Stargate control block update after startup. Increase this if Stargate loads too many chunks during startup. | +| gates.cosmetic | | | +| .rememberDestination | false | Whether the topmost name on a sign's destinations should be the most recently used destination. | +| .sortNetworkDestinations | false | If true, network lists will be sorted alphabetically. | +| .mainSignColor | BLACK | This allows you to specify the color of the gate signs. Use a color code such as WHITE,BLACK,YELLOW or a hex color code such as '#ed76d9'. You need quotes around hex color codes. | +| .highlightSignColor | WHITE | This allows you to specify the color of the sign markings. Use a color code such as WHITE,BLACK,YELLOW or a hex color code such as '#ed76d9'. You need quotes around hex color codes. | +| .perSignColors | \[List] | A list of per-sign color specifications. Format: "SIGN_TYPE:mainColor,highlight_color". The SIGN_TYPE is OAK for an oak sign, DARK_OAK for a dark oak sign and so on. The colors can be "default" to use the color specified in "mainSignColor" or "highlightSignColor", "inverted" to use the inverse color of the default color, a normal color such as BLACK,WHITE,YELLOW or a hex color code such as #ed76d9. | +| economy | | | +| .freeGatesColored | false | Enable to make gates that won't cost the player money show up as green | +| .freeGatesColor | DARK_GREEN | This allows you to specify the color of the markings and name of free stargates | +| .useEconomy | false | Whether or not to enable Economy using Vault (must have the Vault plugin) | +| .createCost | 0 | The cost to create a stargate | +| .destroyCost | 0 | The cost to destroy a stargate (Can be negative for a "refund") | +| .useCost | 0 | The cost to use a stargate | +| .toOwner | false | Whether the money from gate-use goes to the owner or nobody | +| .chargeFreeDestination | true | Enable to make players pay for teleportation even if the destination is free | +| .taxAccount | '' | The tax account to use if the server uses a closed economy. | +| folders | | | +| .portalFolder | plugins/Stargate/portals/ | The folder your portal databases are saved in | +| .gateFolder | plugins/Stargate/gates/ | The folder containing your .gate files | +| debugging | | | +| .debug | false | Whether to show massive debug output | +| .permissionDebug | false | Whether to show massive permission debug output | +| advanced | | | +| .waitForPlayerAfterTeleportDelay | 6 | The amount of ticks to wait before adding a player as passenger of a vehicle. On slow servers, a value of 6 is required to avoid client glitches after teleporting on a vehicle. | # Message Customization -It is possible to customize all the messages Stargate displays, including the \[Stargate] prefix. You can find the -strings in plugins/Stargate/lang/chosenLanguage.txt. +It is possible to customize all the messages Stargate displays, including the \[Stargate] prefix.
You may find these +strings in `plugins/Stargate/lang/chosenLanguage.txt`. -If a string is removed, or left blank, it will default to the default english string. There are some special cases -regarding messages. When you see %variableName%, you need to keep this part in your string, as it will be replaced with -relevant values. +If a string is removed, or left blank, it will default to the default english string.
There are some special cases +Please note that %variableName% should be kept, as it will be replaced with a relevant value. -The full list of strings is as follows: +
+ The full list of strings may be found below: (Click to expand) -``` -prefix=[Stargate] -teleportMsg=Teleported -destroyMsg=Gate Destroyed -invalidMsg=Invalid Destination -blockMsg=Destination Blocked -destEmpty=Destination List Empty -denyMsg=Access Denied -reloaded=Stargate Reloaded +| Path | Text | +|-----------------------|--------------------------------------------------------------------| +| prefix | \[Stargate] | +| teleportMsg | Teleported | +| destroyMsg | Gate Destroyed | +| invalidMsg | Invalid Destination | +| blockMsg | Destination Blocked | +| destEmpty | Destination List Empty | +| denyMsg | Access Denied | +| reloaded | Stargate Reloaded | +| | | +| ecoDeduct | Deducted %cost% | +| ecoRefund | Refunded %cost% | +| ecoObtain | Obtained %cost% from Stargate %portal% | +| ecoInFunds | Insufficient Funds | +| ecoLoadError | Vault was loaded, but no economy plugin could be hooked into | +| vaultLoadError | Economy is enabled but Vault could not be loaded. Economy disabled | +| vaultLoaded | Vault v%version% found | +| | | +| createMsg | Gate Created | +| createNetDeny | You do not have access to that network | +| createGateDeny | You do not have access to that gate layout | +| createPersonal | Creating gate on personal network | +| createNameLength | Name too short or too long. | +| createExists | A gate by that name already exists | +| createFull | This network is full | +| createWorldDeny | You do not have access to that world | +| createConflict | Gate conflicts with existing gate | +| | | +| signRightClick | Right click | +| signToUse | to use gate | +| signRandom | Random | +| signDisconnected | Disconnected | +| signInvalidGate | Invalid gate | +| | | +| bungeeDisabled | BungeeCord support is disabled. | +| bungeeDeny | You do not have permission to create BungeeCord gates. | +| bungeeEmpty | BungeeCord gates require both a destination and network. | +| bungeeSign | Teleport to | +| | | +| portalInfoTitle | \[STARGATE INFO] | +| portalInfoName | Name: %name% | +| portalInfoDestination | Destination: %destination% | +| portalInfoNetwork | Network: %network% | +| portalInfoServer | Server: %server% | -ecoDeduct=Deducted %cost% -ecoRefund=Refunded %cost% -ecoObtain=Obtained %cost% from Stargate %portal% -ecoInFunds=Insufficient Funds -ecoLoadError=Vault was loaded, but no economy plugin could be hooked into -vaultLoadError=Economy is enabled but Vault could not be loaded. Economy disabled -vaultLoaded=Vault v%version% found +
-createMsg=Gate Created -createNetDeny=You do not have access to that network -createGateDeny=You do not have access to that gate layout -createPersonal=Creating gate on personal network -createNameLength=Name too short or too long. -createExists=A gate by that name already exists -createFull=This network is full -createWorldDeny=You do not have access to that world -createConflict=Gate conflicts with existing gate - -signRightClick=Right click -signToUse=to use gate -signRandom=Random -signDisconnected=Disconnected -signInvalidGate=Invalid gate - -bungeeDisabled=BungeeCord support is disabled. -bungeeDeny=You do not have permission to create BungeeCord gates. -bungeeEmpty=BungeeCord gates require both a destination and network. -bungeeSign=Teleport to - -portalInfoTitle=[STARGATE INFO] -portalInfoName=Name: %name% -portalInfoDestination=Destination: %destination% -portalInfoNetwork=Network: %network% -portalInfoServer=Server: %server% -``` +> **PLEASE NOTE**: This method of localisation is slated to change in the upcoming rewrite!
If stargate does not +> currently support your language, please submit a translation [here](https://sgrewritten.org/translate)! # Changes +#### \[Version 0.11.5.11] Unified Legacy Fork + +- Removes legacy sign drawing code, as the check for whether a server supported the sign sides kept failing for some + users. +- Fixes some problems regarding the startup validation of Stargates, which caused valid Stargates to be disabled when + the verification option was enabled. + +#### \[Version 0.11.5.10] Unified Legacy Fork + +- Fixed a problem with material tag support +- Made serveral optimisations to significantly improve performance on servers with high playercounts. + +#### \[Version 0.11.5.9] Unified Legacy Fork + +- Hotfix for the previous release, which accidently broke backwards compatibility for older servers (1.17-1.19). + +#### \[Version 0.11.5.8] Unified Legacy Fork + +- Cleaned up handling of invalid gate files during both migration and startup. +- Backported the rewrite's translation system to legacy: to add new localisations, please + use [this page](https://sgrewritten.org/translate). +- Updated various dependencies. +- Optimisations in the migrator as to mitigate issues associated with importing large past deployments. +- Improved handling of some potential error states involving Floodgate and Geyser. + +#### \[Version 0.11.5.7] Unified Legacy Fork + +- Added support for multiple materials and tags (comma separated). +- Fixed an issue wherein dynmap crashes would cascade to Stargate +- Backported the Rewrite's workaround for unexpected `END_GATEWAY` behaviour impacting geyser players. +- Materials and tags are now handled agnostically; buttons and other generations are now less aggressive. + +#### \[Version 0.11.5.6] Unified Legacy Fork + +- Fixed a potential stack trace experienced when disabling Dynmap +- Fixed some problems related to negative economy transactions. +- Fixed an exception occuring when negative yaw values are encountered. +- Updated the about command and added a debug command. + +#### \[Version 0.11.5.5] Unified Legacy Fork + +- Hotfix for a compatibility issue preventing the plugin from working on most pre-1.20 server jars. + +#### \[Version 0.11.5.4] Unified Legacy Fork + +- Fix for a problem which could cause activators to generate at invalid locations. +- Minor documentation clarification. +- Added support for MineCraft version 1.20 + +#### \[Version 0.11.5.3] Unified Legacy Fork + +- Hotfix for an issue wherein the gate folder failed to populate on fresh installations. + +#### \[Version 0.11.5.2] Unified Legacy Fork + +- Improved handling of incompatible server environments (i.e. CraftBukkit) +- Significantly improved an internal system responsible for yaml migrations. +- Imported the LCLO fork's configuration comments. +- Fixed a problem that caused the update checker to produce false positives. +- Added support for Tax Accounts (part of Towny Closed Economies) + +#### \[Version 0.11.5.1] UNIFIED LEGACY FORK + +- Merged the fork into the [SG Rewritten Project](https://sgrewritten.org) +- *This fork is now the maintained legacy branch of the Stargate Rewritten Project*. + - This plugin will be superseded in the near future by Stargate Rewritten,
a collaborative effort to wholly + rewrite and reimagine Drakia's code base. +- Changed the `E` flag to `V`, to improve consistency for the rewrite. +- Reworked the readme and changed some shortcuts + +#### \[Version 0.10.X.X] LCLO fork + +- Reimplemented/merged + the [LCLO fork ecosystem](https://github.com/stargate-rewritten/Stargate-ESR/tree/ESR-1.13.2-1.16.5), + notably: + - Added material #tag support + - Expanded legacy migration support + - Changed MSV to 1.16 + - Added a new default gate file + - Added bstats + +> **NOTE: The LCLO fork has its own changelog, +found [here](https://github.com/stargate-rewritten/Stargate-ESR#version-01081-lclo-fork)!**
+> For brevity, the full list has been excluded from the below.
+> In general, it is safe to assume **FULL LCLO PARITY** as of this version. + #### \[Version 0.9.4.2] EpicKnarvik97 fork -- Avoids a NullPointerException if Dynmap is present, but isn't properly loaded. -- Avoids some potential NullPointerExceptions related to Dynmap integration -- Fixes end portals hijacking BungeeCord teleportation -- Fixes a problem where a player might not be properly teleported from an end portal Stargate in the end to the - over-world. +- Prevents improperly loaded dependencies from causing problems with SG. +- Improved Dynmap integration and associated bug fixes. +- Fixes for some issues surrounding end portals. #### \[Version 0.9.4.1] EpicKnarvik97 fork -- Reverts to Spigot API 1.18 -- Adds Dynmap integration +- Adds integration with [Dynmap](https://www.spigotmc.org/resources/dynmap%C2%AE.274/) #### \[Version 0.9.4.0] EpicKnarvik97 fork -- Updates Stargate to 1.19 +- Minecraft version 1.19 support. #### \[Version 0.9.3.7] EpicKnarvik97 fork -- Adds the Japanese language file provided by spigot user furplag +- Added a Japanese localisation courtesy of `furplag`.
+ *For more info translation status, please see [this](https://sgrewritten.org/translate)*. #### \[Version 0.9.3.6] EpicKnarvik97 fork -- Adds the simplified Chinese language file provided by spigot user YKDZ +- Added a Chinese (simplified) localisation courtesy of `YKDZ`.
+ *For more info translation status, please see [this](https://sgrewritten.org/translate)*. #### \[Version 0.9.3.5] EpicKnarvik97 fork -- Fixes the wait for player delay being too low by default -- Performs some minor code optimizations and restructuring +- Fixed an issue that could result in invisible players following teleportation. +- Made some minor optimisations and code refactors. #### \[Version 0.9.3.4] EpicKnarvik97 fork -- Includes passengers of passengers when teleporting entities -- Fixes a bug which caused Stargate to use more CPU for no reason -- Teleports boats/minecarts like other vehicles unless *enableCraftBookRemoveOnEjectFix* is enabled -- Adds the *waitForPlayerAfterTeleportDelay* config option which allows changing the delay between vehicle teleportation - and the player being teleported to the vehicle -- Makes boats keep their wood type even when re-created +- Entities will now teleport with any passengers they may have. +- Significantly optimised the plugin's CPU usage. +- Added an alternative transportation method to resolve a CraftBook incompatibility, and an associated config + toggle (`enableCraftBookRemoveOnEjectFix`). +- Added a config option (`waitForPlayerAfterTeleportDelay`) to allow users to specify the delay between vehicle + teleportation and player teleportation. #### \[Version 0.9.3.3] EpicKnarvik97 fork -- Prevents Zombified Piglins from randomly spawning at Stargates +- Prevents Zombified Piglins from spawning at stargates with `nether-portal` irises. #### \[Version 0.9.3.2] EpicKnarvik97 fork -- Adds a config option to set the exit velocity of any players exiting a stargate -- Adjusts vehicle teleportation a bit to prevent passengers' exit rotation from being wrong -- Improves the checking for buggy double-clicks on non-button blocks +- The velocity multiplier applied to users exiting stargates may now be modified through a config option. +- Fixes an issue where players could exit stargates with improper head rotation. +- Fixes an issue where, in certain circumstances, double-clicks on frame (non-activator) blocks could activate a gate. #### \[Version 0.9.3.1] EpicKnarvik97 fork -- Ignores the type of air when checking if a stargate is valid +- Fixed an issue wherein one would be unable to create a stargate due to the presence of nonstandard air. #### \[Version 0.9.3.0] EpicKnarvik97 fork -- Adds support for RGB colors (use hex color codes) -- Adds support for dyed and glowing signs -- Adds support for specifying sign colors per sign type -- Adds a tab-completable config sub-command for easily changing per-sign colors -- Allows a per-sign color to be set as the inverse of the default color of the given type +- Adds full support for the new sign colour features: + - Sign colours may now be specified through the use of RGB colour codes. + - Signs may now be dyed and/or glow-inked + - Colours may now be specified per sign type. +- Adds the "sg config" command to allow users to easily change a given sign's RGB colours. + - One new option for this is "inverse", which inverts the default colour for a specific sign. #### \[Version 0.9.2.5] EpicKnarvik97 fork -- Updates Java version to JDK 17 -- Updates Spigot API version to 1.18 +- Updated plugin to use Java 17 +- Adds support for MineCraft version 1.18 #### \[Version 0.9.2.4] EpicKnarvik97 fork -- Adds update checking, which will display a notice in the console when updates are available -- Adds an alert about an available update when an admin joins the server -- Adds the adminUpdateAlert config option to allow the admin notices to be turned off +- Adds update checker, which will (optionally) display a notice in console and admins whenever a new SG update is + available. +- Adds a config toggle (`adminUpdateAlert`) to disable the above. #### \[Version 0.9.2.3] EpicKnarvik97 fork -- Fixes a typo which caused both colors to change into the highlightSignColor +- Fixes an issue that resulted in `highlightSignColor` being used for all colours. #### \[Version 0.9.2.2] EpicKnarvik97 fork -- Prevents teleportation of a player holding creatures on a leash when handleLeashedCreatures is disabled, to prevent - players accidentally losing the creatures during teleportation -- Fixes a potential exception when a gate's open-block or closed-block is set to a material which isn't a block -- Fixes a potential exception when a portal without a sign has an invalid gate type -- Prevents loading of gate files using non-blocks as part of the border -- Prevents a player smuggling another player through a restricted stargate by sitting on a creature held in a lead by - the first player +- Fixed an issue wherein`handleLeashedCreatures` could cause accidental creature losses when disabled. +- Prevented several potential crashes relating to invalid block and gate types. +- Fixed an issue wherein a players could smuggle each other through Private stargates. #### \[Version 0.9.2.1] EpicKnarvik97 fork -- Makes sure to only reload whatever is necessary when config values are changed using commands, instead of reloading - the entire config file every time -- Protects portals from block placement when protectEntrance is enabled +- Improves the efficiency and stability of the reload command. +- Adds a toggle (`protectEntrance`) to extend portal protection to the iris. #### \[Version 0.9.2.0] EpicKnarvik97 fork - Increases max length of names and networks to 13 characters -- Excludes color codes from the counted character length to allow a colored, 13-character name -- Makes portal names and networks case- and color-agnostic to prevent some confusion caused by typos or sloppy - configuration -- Makes the free gate color configurable, and renames freeGatesGreen to freeGatesColored +- Excludes color codes from the counted character length to allow coloured names with up to 13 characters. +- Fixes an issue wherein typos or mistaken capitalisation could result in the creation of duplicated portals. +- Makes the free gate color configurable, and renames the `freeGatesGreen` toggle to `freeGatesColored`. #### \[Version 0.9.1.2] EpicKnarvik97 fork -- Allows a sneaking player to see information about a silent stargate with no sign +- Players may now see information about stargates (especially V gates) by sneaking and right-clicking. #### \[Version 0.9.1.1] EpicKnarvik97 fork -- Makes sure to translate the `&` character to fix a bug causing portal signs to not be colored on some servers +- Fixed a bug where sign colouring failed due to improper translation of the `&` symbol. #### \[Version 0.9.1.0] EpicKnarvik97 fork -- Rewrites config loading as a part of the changes required to implement config commands -- This update adds commands to change all config values from the chat or the console, complete with tab completion -- Adds a new permission "stargate.admin.config" which is required to edit config values from the chat +- Refactors the configuration loading systems as to facilitate the below: +- Added the `sg config` command, to allow for all config values to be changed in-game by users with permission. + - The relevant permission is `stargate.admin.config`. #### \[Version 0.9.0.7] EpicKnarvik97 fork -- Stops registering the sign as a lookup block for stargates without a sign -- Only removes a stargate's button if it's actually a button-compatible block -- Only displays portal info if not placing a block +- Fixes an issue involving sign registration for V gates. +- Prevents a situation that may result in an invalid button state. +- Tweaks when portal information is displayed to prevent an inconvenient conflict with block placement. #### \[Version 0.9.0.6] EpicKnarvik97 fork -- Makes containers no longer open when used as buttons -- Validates and updates stargate buttons when the plugin is loaded or reloaded -- Adds an option to make a stargate silent (no text in chat when teleporting) for better immersion on RP servers -- Makes buttons update and/or remove themselves when their location or material changes -- Adds another default gate to show that it's possible to use any number of materials for a stargate's border -- Adds an option for stargates without a sign. Right-clicking such a stargate will display gate information -- Fixes a bug causing signs to be re-drawn after they're broken -- Makes buttons and signs be replaced by water instead of air when underwater -- Makes portal info shown when right-clicking a stargate fully customizable +- Prevents containers used as buttons from displaying the opening animation. +- Improved load-time gate and button validation. +- Adds the `Q` flag (suppresses chat messages related to portal use -- perfect for RP servers!). +- Prevents a bug where buttons could incorrect overwrite materials. +- Adds an additional default gate as to explain multi-material stargates. +- Adds the `V` flag (hides the stargate's sign -- right-clicking will display the relevant information in chat). + - This is presented as an alternative for the `B` flag. +- Fixes a bug where, in certain circumstances, signs could become unbreakable. +- Improves how waterlogging is handled. +- Right-clicking a stargate's frame will now display information in chat. #### \[Version 0.9.0.5] EpicKnarvik97 fork -- Adds an option to stargate functionality to disable all teleportation of creatures -- Adds an option to stargate functionality to disable all teleportation of empty minecarts -- Adds an option to stargate functionality to disable teleportation of creatures if no player is present in the vehicle -- Prevents a player in a vehicle from teleporting without the vehicle if vehicle teleportation is disabled -- Prevents an infinite number of teleportation messages if vehicle teleportation is detected but denied +- Adds configuration toggles for: + - Whether living non-player entities may be teleported. + - Whether vehicles may teleport without a player riding them. + - Whether vehicles may teleport living non-player entities if accompanied by a player rider. +- Fixes a bug that could result in unauthorised teleportation. +- Fixes a bug that, in certain circumstances, could result in chat spam. #### \[Version 0.9.0.4] EpicKnarvik97 fork -- Adds teleportation of leashed creatures. By default, any creature connected to a player by a lead will be teleported - with the player through stargates, even if the player is in a vehicle. This behavior can be disabled in the config - file. +- Leashed entities will now teleport with their associated player, even when that player is in a vehicle! +- Added a configuration toggle for this feature. #### \[Version 0.9.0.3] EpicKnarvik97 fork -- Adds a missing error message when a player in a vehicle cannot pay the teleportation fee -- Adds UUID migration to automatically update player names to UUID when possible +- An error message has been clarified as to better account for vehicles. +- Players with legacy string IDs will now be automatically converted to modern UUIDs upon joining. #### \[Version 0.9.0.2] EpicKnarvik97 fork -- Fixes a bug causing Stargates using NETHER_PORTAL blocks to generate nether portals in the nether. +- Fixes a bug that could result in unwanted portals to generate in the nether. #### \[Version 0.9.0.1] EpicKnarvik97 fork -- Adds the highlightSignColor option and renames the signColor option to mainSignColor -- Fixes some inconsistencies in sign coloring by using the highlight color for all markings -- Fixes the order in which configs are loaded to prevent an exception -- Adds migrations for the config change +- Added coloured highlight patterns to signs: `highlightSignColor` has been added, and `signColor` has been renamed + to `mainSignColor`. +- Addressed some inconsistencies in sign coloring by using the highlight color for all markings +- Fixed some issues pertaining to configuration handling and management. #### \[Version 0.9.0.0] EpicKnarvik97 fork -- Changes entire path structure to a more modern and maven-compliant one -- Changes package structure to net.knarcraft.stargate.* -- Moves language files into the resources folder -- Fixes some bugs caused by language files not being read as UTF-8 -- Adds JavaDoc to a lot of the code -- Adds Norwegian translation for both Norwegian languages -- Adds missing dependency information to plugin.yml -- Uses text from the language files in more places -- Changes how backup language works, causing english strings to be shown if not available from the chosen language -- Removes some pre-UUID code -- Adds underwater portals -- Makes it easier to add more default gates -- Adds a new default gate which can be used underwater -- Adds more items usable as buttons (corals, chest, shulker-box), which allows underwater portals -- Splits a lot of the code into smaller objects -- Moves duplicated code into helper classes -- Re-implements vehicle teleportation -- Makes boat teleportation work as expected, including being able to teleport with two passengers. This allows players - to use boats to transport creatures through portals and to other areas, or even worlds -- Makes it possible to teleport a player riding a living entity (a pig, a horse, a donkey, a zombie horse, a skeleton - horse or a strider). It does not work for entities the player cannot control, such as llamas. -- Makes both nether portals and end gateways work properly without causing mayhem -- Replaces the modX and modZ stuff with yaw calculation to make it easier to understand -- Comments all the code -- Extracts portal options and portal-related locations to try and reduce size -- Rewrites tons of code to make it more readable and manageable -- Implements proper snowman snow blocking, and removes the "temporary" ignoreEntrances option -- Adds a default gate using end stone bricks and end gateway for more default diversity -- Makes portals using end portal blocks work as expected -- Adds missing permissions to the readme -- Adds missing permissions to plugin.yml and simplifies permission checks by specifying default values for child - permissions -- Renames stargate.reload to stargate.admin.reload to maintain consistency -- Marks stargates which cannot be loaded because of the gate layout not having been loaded -- Uses white for the "-" characters on the side of each stargate name when drawing signs to increase readability -- Uses white to mark the selected destination when cycling through stargate destinations +- Significantly refactored the legacy codebase for readability and quality. + - Added developer documentation. +- Changed the package to net.knarcraft.stargate.* +- Improved localisations and their handling. + - Added several new translations. +- Fixed some encoding problems. +- Added some missing dependencies, updated the project's plugin file. +- Made underwater portals practical by reworking activator materials and adding support for waterlogging. +- Updated the vehicle teleportation code to work in modern versions of the game, and addressed CVE-2021-43819. +- Updated boat teleportation to account for multi-passenger vehicles. +- Added support for passengers and leashed entities. +- Fixed some issues surrounding block states and data (notably, nether portals and end gateways). +- Overhauled the plugin's handling of movement. +- Slightly improved the plugin's flatfile storage structure. +- Improved portal protection, implemented many associated "TODOs" +- Adds another default gate to illustrate multi-type designs. +- Significantly improved the plugin's documentation via README +- Properly implemented a load of missing permissions -- especially including handling of child nodes. +- Renamed the stargate.reload node to stargate.admin.reload as to improve consistency +- Improves the stability of Stargate's load-time portal handling. +- Highlights destination selector brackets on signs ("-") as to improve readability. - Uses dark red to mark portals which are inactive (missing destination or invalid gate type) -- Re-draws signs on startup in case they change -- Fixes some bugs preventing changing the portal-open block on the fly -- Adds a translate-able string for when the plugin has been reloaded +- Adds provision to re-draw incorrect signs. +- Fixed a load of other miscellaneous bugs. #### \[Version 0.8.0.3] PseudoKnight fork @@ -1200,4 +1405,4 @@ portalInfoServer=Server: %server% - Changed package to net.TheDgtl.* - Everything now uses Blox instead of Block objects -- Started on vehicle code, but it's still buggy \ No newline at end of file +- Started on vehicle code, but it's still buggy diff --git a/pom.xml b/pom.xml index 2e1bead..276595c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ net.knarcraft Stargate - 0.9.4.3-SNAPSHOT + 0.11.5.11 @@ -28,8 +28,8 @@ https://hub.spigotmc.org/nexus/content/groups/public/ - vault-repo - https://nexus.hc.to/content/repositories/pub_releases + jitpack.io + https://jitpack.io dynmap @@ -39,67 +39,82 @@ papermc https://repo.papermc.io/repository/maven-public/ - - - knarcraft-repo - https://git.knarcraft.net/api/packages/EpicKnarvik97/maven + minebench-repo + https://repo.minebench.de/ - - knarcraft-repo - https://git.knarcraft.net/api/packages/EpicKnarvik97/maven - - + + opencollab-snapshot + https://repo.opencollab.dev/main/ + + org.spigotmc spigot-api - 1.19.3-R0.1-SNAPSHOT + 1.21.8-R0.1-SNAPSHOT provided - net.milkbowl.vault + com.github.MilkBowl VaultAPI - 1.7 + 1.7.1 provided - - org.junit.jupiter - junit-jupiter-api - 5.9.0 - test - - - com.github.seeseemelk - MockBukkit-v1.18 - 2.85.2 - test - org.jetbrains annotations - 23.0.0 + 26.0.0 provided - - junit - junit - 4.13.2 - test - us.dynmap dynmap-api - 3.1-beta-2 + 3.5 provided net.knarcraft knarlib - 1.0-SNAPSHOT + 1.2.18 compile + + org.bstats + bstats-bukkit + 3.0.2 + compile + + + org.bstats + bstats-base + 3.0.2 + + + de.themoep + minedown + 1.7.1-SNAPSHOT + compile + + + org.geysermc.geyser + api + 2.2.0-SNAPSHOT + provided + + + org.geysermc.floodgate + api + 2.2.2-SNAPSHOT + provided + + + org.junit.jupiter + junit-jupiter + 5.11.4 + test + @@ -116,7 +131,8 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.4.1 + package @@ -125,7 +141,37 @@ false + + + org.bstats + net.knarcraft.stargate.lib.metrics + + + net.knarcraft.knarlib + net.knarcraft.stargate.lib.knarlib + + + de.themoep + net.knarcraft.stargate.lib.minedown + + + org.jetbrains.annotations + net.knarcraft.blacksmith.lib.annotations + + + + de.themoep:minedown + + de/themoep/minedown/** + + + + org.bstats + + org/bstats/** + + net.knarcraft:knarlib @@ -133,10 +179,10 @@ - - *.MF - *.yml - + org.jetbrains:annotations + + org/jetbrains/annotations/** + @@ -151,4 +197,4 @@ - \ No newline at end of file + diff --git a/src/main/java/net/knarcraft/stargate/Stargate.java b/src/main/java/net/knarcraft/stargate/Stargate.java index 2cb40b1..3a3dec8 100644 --- a/src/main/java/net/knarcraft/stargate/Stargate.java +++ b/src/main/java/net/knarcraft/stargate/Stargate.java @@ -1,14 +1,20 @@ package net.knarcraft.stargate; +import net.knarcraft.knarlib.formatting.StringFormatter; +import net.knarcraft.knarlib.formatting.Translator; +import net.knarcraft.knarlib.plugin.ConfigCommentPlugin; +import net.knarcraft.knarlib.util.ConfigHelper; import net.knarcraft.knarlib.util.UpdateChecker; import net.knarcraft.stargate.command.CommandStarGate; import net.knarcraft.stargate.command.StarGateTabCompleter; import net.knarcraft.stargate.config.EconomyConfig; -import net.knarcraft.stargate.config.MessageSender; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.config.StargateConfig; import net.knarcraft.stargate.config.StargateGateConfig; import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.ChunkUnloadRequest; +import net.knarcraft.stargate.container.ControlBlockUpdateRequest; import net.knarcraft.stargate.listener.BlockEventListener; import net.knarcraft.stargate.listener.EntityEventListener; import net.knarcraft.stargate.listener.EntitySpawnListener; @@ -22,15 +28,16 @@ import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.thread.BlockChangeThread; import net.knarcraft.stargate.thread.ChunkUnloadThread; +import net.knarcraft.stargate.thread.ControlBlocksUpdateThread; import net.knarcraft.stargate.thread.StarGateThread; -import org.bukkit.Server; -import org.bukkit.command.PluginCommand; -import org.bukkit.configuration.file.FileConfiguration; +import net.knarcraft.stargate.utility.BStatsHelper; +import org.bukkit.Bukkit; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.scheduler.BukkitScheduler; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.LinkedList; @@ -45,7 +52,7 @@ Copyright (C) 2011 Shaun (sturmeh) Copyright (C) 2011 Dinnerbone Copyright (C) 2011-2013 Steven "Drakia" Scott Copyright (C) 2015-2020 Michael Smith (PseudoKnight) -Copyright (C) 2021-2022 Kristian Knarvik (EpicKnarvik97) +Copyright (C) 2021-2025 Kristian Knarvik (EpicKnarvik97) The following license notice applies to all source and resource files in the Stargate project: @@ -67,9 +74,11 @@ along with this program. If not, see . * The main class of the Stargate plugin */ @SuppressWarnings("unused") -public class Stargate extends JavaPlugin { +public class Stargate extends ConfigCommentPlugin { - private static final Queue blockChangeRequestQueue = new LinkedList<>(); + private static final String CONFIG_FILE_NAME = "config.yml"; + private static final Queue controlBlockUpdateRequestQueue = new LinkedList<>(); + private static final Queue CONTROL_BLOCK_UPDATE_REQUEST_QUEUE = new LinkedList<>(); private static final Queue chunkUnloadQueue = new PriorityQueue<>(); private static Logger logger; @@ -105,7 +114,7 @@ public class Stargate extends JavaPlugin { * * @param version

The version of the new update available

*/ - public static void setUpdateAvailable(String version) { + public static void setUpdateAvailable(@NotNull String version) { updateAvailable = version; } @@ -114,6 +123,7 @@ public class Stargate extends JavaPlugin { * * @return

The version number if an update is available. Null otherwise

*/ + @Nullable public static String getUpdateAvailable() { return updateAvailable; } @@ -123,6 +133,7 @@ public class Stargate extends JavaPlugin { * * @return

An instance of this plugin, or null if not instantiated

*/ + @NotNull public static Stargate getInstance() { return stargate; } @@ -132,28 +143,41 @@ public class Stargate extends JavaPlugin { * * @param request

The request to add

*/ - public static void addBlockChangeRequest(BlockChangeRequest request) { + public static void addControlBlockUpdateRequest(@Nullable BlockChangeRequest request) { if (request != null) { - blockChangeRequestQueue.add(request); + controlBlockUpdateRequestQueue.add(request); } } /** - * Gets the queue containing block change requests + * Gets the queue containing control block update requests * - * @return

A block change request queue

+ * @return

A control block update request queue

*/ - public static Queue getBlockChangeRequestQueue() { - return blockChangeRequestQueue; + @NotNull + public static Queue getControlBlockUpdateRequestQueue() { + return controlBlockUpdateRequestQueue; } /** - * Gets the sender for sending messages to players + * Adds a control block update request to the request queue * - * @return

The sender for sending messages to players

+ * @param request

The request to add

*/ - public static MessageSender getMessageSender() { - return stargateConfig.getMessageSender(); + public static void addControlBlockUpdateRequest(@Nullable ControlBlockUpdateRequest request) { + if (request != null) { + CONTROL_BLOCK_UPDATE_REQUEST_QUEUE.add(request); + } + } + + /** + * Gets the queue containing button update requests + * + * @return

A button update request queue

+ */ + @NotNull + public static Queue getButtonUpdateRequestQueue() { + return CONTROL_BLOCK_UPDATE_REQUEST_QUEUE; } /** @@ -161,6 +185,7 @@ public class Stargate extends JavaPlugin { * * @return

The object containing gate configuration values

*/ + @NotNull public static StargateGateConfig getGateConfig() { return stargateConfig.getStargateGateConfig(); } @@ -170,6 +195,7 @@ public class Stargate extends JavaPlugin { * * @return

This plugin's version

*/ + @NotNull public static String getPluginVersion() { return pluginVersion; } @@ -179,6 +205,7 @@ public class Stargate extends JavaPlugin { * * @return

The logger

*/ + @NotNull public static Logger getConsoleLogger() { return logger; } @@ -199,8 +226,8 @@ public class Stargate extends JavaPlugin { * @param route

The class name/route where something happened

* @param message

A message describing what happened

*/ - public static void debug(String route, String message) { - if (stargateConfig == null || stargateConfig.isDebuggingEnabled()) { + public static void debug(@NotNull String route, @NotNull String message) { + if (stargateConfig == null || stargateConfig.isNotLoaded() || stargateConfig.isDebuggingEnabled()) { logger.info("[Stargate::" + route + "] " + message); } else { logger.log(Level.FINEST, "[Stargate::" + route + "] " + message); @@ -212,8 +239,8 @@ public class Stargate extends JavaPlugin { * * @param message

The message to log

*/ - public static void logInfo(String message) { - logger.info(getBackupString("prefix") + message); + public static void logInfo(@NotNull String message) { + log(Level.INFO, message); } /** @@ -221,7 +248,7 @@ public class Stargate extends JavaPlugin { * * @param message

The message to log

*/ - public static void logSevere(String message) { + public static void logSevere(@NotNull String message) { log(Level.SEVERE, message); } @@ -230,7 +257,7 @@ public class Stargate extends JavaPlugin { * * @param message

The message to log

*/ - public static void logWarning(String message) { + public static void logWarning(@NotNull String message) { log(Level.WARNING, message); } @@ -240,8 +267,11 @@ public class Stargate extends JavaPlugin { * @param severity

The severity of the event triggering the message

* @param message

The message to log

*/ - private static void log(Level severity, String message) { - logger.log(severity, getBackupString("prefix") + message); + private static void log(@NotNull Level severity, @NotNull String message) { + if (logger == null) { + logger = Bukkit.getLogger(); + } + logger.log(severity, message); } /** @@ -251,6 +281,7 @@ public class Stargate extends JavaPlugin { * * @return

The folder for storing the portal database

*/ + @NotNull public static String getPortalFolder() { return stargateConfig.getPortalFolder(); } @@ -262,6 +293,7 @@ public class Stargate extends JavaPlugin { * * @return

The folder storing gate files

*/ + @NotNull public static String getGateFolder() { return stargateConfig.getGateFolder(); } @@ -271,51 +303,27 @@ public class Stargate extends JavaPlugin { * * @return

The default network

*/ + @NotNull public static String getDefaultNetwork() { return stargateConfig.getStargateGateConfig().getDefaultPortalNetwork(); } /** - * Gets a translated string given its string key - * - *

The name/key is the string before the equals sign in the language files

- * - * @param name

The name/key of the string to get

- * @return

The full translated string

- */ - public static String getString(String name) { - return stargateConfig.getLanguageLoader().getString(name); - } - - /** - * Gets a backup string given its string key - * - *

The name/key is the string before the equals sign in the language files

+ * Gets a backup string given its message key * * @param name

The name/key of the string to get

* @return

The full string in the backup language (English)

*/ - public static String getBackupString(String name) { + public static @NotNull String getBackupString(@NotNull Message name) { return stargateConfig.getLanguageLoader().getBackupString(name); } - /** - * Replaces a variable in a string - * - * @param input

The input containing the variables

- * @param search

The variable to replace

- * @param value

The replacement value

- * @return

The input string with the search replaced with value

- */ - public static String replaceVars(String input, String search, String value) { - return input.replace(search, value); - } - /** * Gets this plugin's plugin manager * * @return

A plugin manager

*/ + @NotNull public static PluginManager getPluginManager() { return pluginManager; } @@ -325,6 +333,7 @@ public class Stargate extends JavaPlugin { * * @return

The object containing economy config values

*/ + @NotNull public static EconomyConfig getEconomyConfig() { return stargateConfig.getEconomyConfig(); } @@ -333,24 +342,34 @@ public class Stargate extends JavaPlugin { public void onDisable() { PortalHandler.closeAllPortals(); PortalRegistry.clearPortals(); - stargateConfig.clearManagedWorlds(); + if (stargateConfig != null) { + stargateConfig.clearManagedWorlds(); + } getServer().getScheduler().cancelTasks(this); } @Override public void onEnable() { + Stargate.stargate = this; + Stargate.logger = getLogger(); + ConfigHelper.saveDefaults(this); PluginDescriptionFile pluginDescriptionFile = this.getDescription(); pluginManager = getServer().getPluginManager(); - FileConfiguration newConfig = this.getConfig(); - this.saveDefaultConfig(); - newConfig.options().copyDefaults(true); - logger = Logger.getLogger("Minecraft"); - Server server = getServer(); - stargate = this; + // Set temporary string formatter before strings are loaded + SGFormatBuilder.setStringFormatter(new StringFormatter(this.getDescription().getName(), new Translator())); - stargateConfig = new StargateConfig(logger); - stargateConfig.finishSetup(); + try { + stargateConfig = new StargateConfig(logger); + stargateConfig.finishSetup(); + } catch (NoClassDefFoundError exception) { + logSevere("Could not properly load. Class not found: " + + exception.getMessage() + "\nThis is probably because you are using CraftBukkit, or other outdated" + + "Minecraft server software. Minecraft server software based on Spigot or Paper is required. Paper" + + " is recommended, and can be downloaded at: https://papermc.io/downloads/paper"); + this.onDisable(); + return; + } pluginVersion = pluginDescriptionFile.getVersion(); @@ -362,11 +381,13 @@ public class Stargate extends JavaPlugin { //Run necessary threads runThreads(); - this.registerCommands(); + registerCommand("stargate", new CommandStarGate(this), new StarGateTabCompleter()); //Check for any available updates - UpdateChecker.checkForUpdate(this, "https://api.spigotmc.org/legacy/update.php?resource=97784", + UpdateChecker.checkForUpdate(this, "https://api.spigotmc.org/legacy/update.php?resource=109355", Stargate::getPluginVersion, Stargate::setUpdateAvailable); + + BStatsHelper.initialize(this); } /** @@ -376,6 +397,8 @@ public class Stargate extends JavaPlugin { BukkitScheduler scheduler = getServer().getScheduler(); scheduler.runTaskTimer(this, new StarGateThread(), 0L, 100L); scheduler.runTaskTimer(this, new BlockChangeThread(), 0L, 1L); + scheduler.runTaskTimer(this, new ControlBlocksUpdateThread(), 0L, + getStargateConfig().getStargateGateConfig().controlUpdateDelay()); scheduler.runTaskTimer(this, new ChunkUnloadThread(), 0L, 100L); } @@ -395,22 +418,12 @@ public class Stargate extends JavaPlugin { pluginManager.registerEvents(new EntitySpawnListener(), this); } - /** - * Registers a command for this plugin - */ - private void registerCommands() { - PluginCommand stargateCommand = this.getCommand("stargate"); - if (stargateCommand != null) { - stargateCommand.setExecutor(new CommandStarGate()); - stargateCommand.setTabCompleter(new StarGateTabCompleter()); - } - } - /** * Gets the chunk unload queue containing chunks to unload * * @return

The chunk unload queue

*/ + @NotNull public static Queue getChunkUnloadQueue() { return chunkUnloadQueue; } @@ -420,7 +433,7 @@ public class Stargate extends JavaPlugin { * * @param request

The new chunk unload request to add

*/ - public static void addChunkUnloadRequest(ChunkUnloadRequest request) { + public static void addChunkUnloadRequest(@NotNull ChunkUnloadRequest request) { chunkUnloadQueue.removeIf((item) -> item.getChunkToUnload().equals(request.getChunkToUnload())); chunkUnloadQueue.add(request); } @@ -430,6 +443,7 @@ public class Stargate extends JavaPlugin { * * @return

The stargate configuration

*/ + @NotNull public static StargateConfig getStargateConfig() { return stargateConfig; } diff --git a/src/main/java/net/knarcraft/stargate/command/CommandAbout.java b/src/main/java/net/knarcraft/stargate/command/CommandAbout.java index f0acd50..715e656 100644 --- a/src/main/java/net/knarcraft/stargate/command/CommandAbout.java +++ b/src/main/java/net/knarcraft/stargate/command/CommandAbout.java @@ -1,12 +1,20 @@ package net.knarcraft.stargate.command; +import de.themoep.minedown.MineDown; +import net.knarcraft.knarlib.util.FileHelper; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + /** * This command represents the plugin's about command */ @@ -18,11 +26,19 @@ public class CommandAbout implements CommandExecutor { ChatColor textColor = ChatColor.GOLD; ChatColor highlightColor = ChatColor.GREEN; - commandSender.sendMessage(textColor + "Stargate Plugin originally created by " + highlightColor + - "Drakia" + textColor + ", and revived by " + highlightColor + "EpicKnarvik97"); - commandSender.sendMessage(textColor + "Go to " + highlightColor + - "https://git.knarcraft.net/EpicKnarvik97/Stargate " + textColor + "for the official repository"); - String author = Stargate.getStargateConfig().getLanguageLoader().getString("author"); + + + try (InputStream inputStream = FileHelper.getInputStreamForInternalFile("/messages/about.md")) { + if (inputStream != null) { + List lines = FileHelper.readLines(FileHelper.getBufferedReaderFromInputStream(inputStream)); + String aboutMessageString = String.join("\n", lines); + BaseComponent[] component = MineDown.parse(aboutMessageString); + commandSender.spigot().sendMessage(component); + } + } catch (IOException ioException) { + commandSender.sendMessage("Internal error"); + } + String author = Stargate.getStargateConfig().getLanguageLoader().getString(Message.AUTHOR); if (!author.isEmpty()) { commandSender.sendMessage(textColor + "Language created by " + highlightColor + author); } diff --git a/src/main/java/net/knarcraft/stargate/command/CommandConfig.java b/src/main/java/net/knarcraft/stargate/command/CommandConfig.java index 6374518..dd5d5d2 100644 --- a/src/main/java/net/knarcraft/stargate/command/CommandConfig.java +++ b/src/main/java/net/knarcraft/stargate/command/CommandConfig.java @@ -4,7 +4,9 @@ import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.config.ConfigOption; import net.knarcraft.stargate.config.ConfigTag; import net.knarcraft.stargate.config.DynmapManager; +import net.knarcraft.stargate.config.Message; import net.knarcraft.stargate.config.OptionDataType; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.PortalSignDrawer; @@ -16,6 +18,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -30,7 +33,7 @@ public class CommandConfig implements CommandExecutor { @NotNull String[] args) { if (commandSender instanceof Player player) { if (!player.hasPermission("stargate.admin.config")) { - Stargate.getMessageSender().sendErrorMessage(commandSender, "Permission Denied"); + new SGFormatBuilder("Permission Denied").error(commandSender); return true; } } @@ -65,7 +68,8 @@ public class CommandConfig implements CommandExecutor { * @param commandSender

The command sender that changed the value

* @param value

The new value of the config option

*/ - private void updateConfigValue(ConfigOption selectedOption, CommandSender commandSender, String value) { + private void updateConfigValue(@NotNull ConfigOption selectedOption, @NotNull CommandSender commandSender, + @NotNull String value) { FileConfiguration configuration = Stargate.getInstance().getConfig(); //Validate any sign colors @@ -119,7 +123,8 @@ public class CommandConfig implements CommandExecutor { * @param value

The new value of the config option

* @param configuration

The configuration file to save to

*/ - private void updateBooleanConfigValue(ConfigOption selectedOption, String value, FileConfiguration configuration) { + private void updateBooleanConfigValue(@NotNull ConfigOption selectedOption, @NotNull String value, + @NotNull FileConfiguration configuration) { boolean newValue = Boolean.parseBoolean(value); if (selectedOption == ConfigOption.ENABLE_BUNGEE && newValue != Stargate.getGateConfig().enableBungee()) { Stargate.getStargateConfig().startStopBungeeListener(newValue); @@ -135,7 +140,8 @@ public class CommandConfig implements CommandExecutor { * @param commandSender

The command sender that changed the value

* @param value

The new value of the config option

*/ - private void updateStringConfigValue(ConfigOption selectedOption, CommandSender commandSender, String value) { + private void updateStringConfigValue(@NotNull ConfigOption selectedOption, @NotNull CommandSender commandSender, + @NotNull String value) { if (selectedOption == ConfigOption.GATE_FOLDER || selectedOption == ConfigOption.PORTAL_FOLDER || selectedOption == ConfigOption.DEFAULT_GATE_NETWORK) { if (value.contains("../") || value.contains("..\\")) { @@ -161,13 +167,13 @@ public class CommandConfig implements CommandExecutor { * @param commandSender

The command sender that changed the value

* @param arguments

The arguments for the new config option

*/ - private void updateListConfigValue(ConfigOption selectedOption, CommandSender commandSender, String[] arguments) { + private void updateListConfigValue(@NotNull ConfigOption selectedOption, @NotNull CommandSender commandSender, + @NotNull String[] arguments) { FileConfiguration configuration = Stargate.getInstance().getConfig(); if (selectedOption == ConfigOption.PER_SIGN_COLORS) { if (arguments.length < 4) { - Stargate.getMessageSender().sendErrorMessage(commandSender, "Usage: /sg config perSignColors " + - " "); + new SGFormatBuilder("Usage: /sg config perSignColors ").error(commandSender); return; } @@ -190,22 +196,24 @@ public class CommandConfig implements CommandExecutor { * @param arguments

The arguments given by the user

* @return

The per-sign color string to update with, or null if the input was invalid

*/ - private String parsePerSignColorInput(CommandSender commandSender, String[] arguments) { + @Nullable + private String parsePerSignColorInput(@NotNull CommandSender commandSender, @NotNull String[] arguments) { //Make sure the sign type is an actual sign if (Material.matchMaterial(arguments[1] + "_SIGN") == null) { - Stargate.getMessageSender().sendErrorMessage(commandSender, "The given sign type is invalid"); + new SGFormatBuilder("The given sign type is invalid").error(commandSender); return null; } String colorString = arguments[1] + ":"; //Validate the colors given by the user - String[] errorMessage = new String[]{"The given main sign color is invalid!", "The given highlight sign color is invalid!"}; + String[] errorMessage = new String[]{"The given main sign color is invalid!", "The given highlight sign color " + + "is invalid!"}; String[] newColors = new String[2]; for (int i = 0; i < 2; i++) { if (validatePerSignColor(arguments[i + 2])) { newColors[i] = arguments[i + 2]; } else { - Stargate.getMessageSender().sendErrorMessage(commandSender, errorMessage[i]); + new SGFormatBuilder(errorMessage[i]).error(commandSender); return null; } } @@ -220,9 +228,11 @@ public class CommandConfig implements CommandExecutor { * @param colorString

The new color string to replace any previous value with

* @param configuration

The file configuration to update with the new per-sign colors

*/ - private void updatePerSignColors(String signType, String colorString, FileConfiguration configuration) { + private void updatePerSignColors(@NotNull String signType, @NotNull String colorString, + @NotNull FileConfiguration configuration) { List newColorStrings = new ArrayList<>(); - List oldColors = (List) Stargate.getStargateConfig().getConfigOptionsReference().get(ConfigOption.PER_SIGN_COLORS); + List oldColors = (List) Stargate.getStargateConfig().getConfigOptionsReference().get( + ConfigOption.PER_SIGN_COLORS); for (Object object : oldColors) { newColorStrings.add(String.valueOf(object)); } @@ -239,7 +249,7 @@ public class CommandConfig implements CommandExecutor { * @param color

The color chosen by the user

* @return

True if the given color is valid

*/ - private boolean validatePerSignColor(String color) { + private boolean validatePerSignColor(@NotNull String color) { ChatColor newHighlightColor = parseColor(color); return newHighlightColor != null || color.equalsIgnoreCase("default") || color.equalsIgnoreCase("inverted"); @@ -251,11 +261,11 @@ public class CommandConfig implements CommandExecutor { * @param selectedOption

The config option that was changed

* @param commandSender

The command sender that executed the config command

*/ - private void saveAndReload(ConfigOption selectedOption, CommandSender commandSender) { + private void saveAndReload(@NotNull ConfigOption selectedOption, @NotNull CommandSender commandSender) { //Save the config file and reload if necessary Stargate.getInstance().saveConfig(); - Stargate.getMessageSender().sendSuccessMessage(commandSender, "Config updated"); + new SGFormatBuilder("Config updated").success(commandSender); //Reload whatever is necessary reloadIfNecessary(commandSender, selectedOption); @@ -268,7 +278,8 @@ public class CommandConfig implements CommandExecutor { * @param commandSender

The command sender to alert if the color is invalid

* @param value

The new option value

*/ - private boolean registerColor(ConfigOption selectedOption, String value, CommandSender commandSender) { + private boolean registerColor(@NotNull ConfigOption selectedOption, @NotNull String value, + @NotNull CommandSender commandSender) { ChatColor parsedColor = parseColor(value); if (parsedColor == null) { commandSender.sendMessage(ChatColor.RED + "Invalid color given"); @@ -291,7 +302,8 @@ public class CommandConfig implements CommandExecutor { * @param value

The value to parse

* @return

The parsed color or null

*/ - private ChatColor parseColor(String value) { + @Nullable + private ChatColor parseColor(@NotNull String value) { try { return ChatColor.of(value.toUpperCase()); } catch (IllegalArgumentException | NullPointerException ignored) { @@ -307,7 +319,9 @@ public class CommandConfig implements CommandExecutor { * @param value

The value given

* @return

An integer, or null if it was invalid

*/ - private Integer getInteger(CommandSender commandSender, ConfigOption selectedOption, String value) { + @Nullable + private Integer getInteger(@NotNull CommandSender commandSender, @NotNull ConfigOption selectedOption, + @NotNull String value) { try { int intValue = Integer.parseInt(value); @@ -331,7 +345,9 @@ public class CommandConfig implements CommandExecutor { * @param value

The value given

* @return

A double, or null if it was invalid

*/ - private Double getDouble(CommandSender commandSender, ConfigOption selectedOption, String value) { + @Nullable + private Double getDouble(@NotNull CommandSender commandSender, @NotNull ConfigOption selectedOption, + @NotNull String value) { try { double doubleValue = Double.parseDouble(value); @@ -353,7 +369,7 @@ public class CommandConfig implements CommandExecutor { * @param commandSender

The command sender initiating the reload

* @param configOption

The changed config option

*/ - private void reloadIfNecessary(CommandSender commandSender, ConfigOption configOption) { + private void reloadIfNecessary(@NotNull CommandSender commandSender, @NotNull ConfigOption configOption) { if (ConfigTag.requiresFullReload(configOption)) { //Reload everything Stargate.getStargateConfig().reload(commandSender); @@ -391,7 +407,7 @@ public class CommandConfig implements CommandExecutor { * @param sender

The command sender that sent the command

* @param option

The config option to print information about

*/ - private void printConfigOptionValue(CommandSender sender, ConfigOption option) { + private void printConfigOptionValue(@NotNull CommandSender sender, @NotNull ConfigOption option) { Object value = Stargate.getStargateConfig().getConfigOptions().get(option); sender.sendMessage(getOptionDescription(option)); sender.sendMessage(ChatColor.GREEN + "Current value: " + ChatColor.GOLD + value); @@ -402,8 +418,8 @@ public class CommandConfig implements CommandExecutor { * * @param sender

The command sender to display the config list to

*/ - private void displayConfigValues(CommandSender sender) { - sender.sendMessage(ChatColor.GREEN + Stargate.getBackupString("prefix") + ChatColor.GOLD + + private void displayConfigValues(@NotNull CommandSender sender) { + sender.sendMessage(ChatColor.GREEN + Stargate.getBackupString(Message.PREFIX) + ChatColor.GOLD + "Config values:"); for (ConfigOption option : ConfigOption.values()) { sender.sendMessage(getOptionDescription(option)); @@ -416,7 +432,8 @@ public class CommandConfig implements CommandExecutor { * @param option

The option to describe

* @return

A string describing the config option

*/ - private String getOptionDescription(ConfigOption option) { + @NotNull + private String getOptionDescription(@NotNull ConfigOption option) { Object defaultValue = option.getDefaultValue(); String stringValue = String.valueOf(defaultValue); if (option.getDataType() == OptionDataType.STRING_LIST) { diff --git a/src/main/java/net/knarcraft/stargate/command/CommandReload.java b/src/main/java/net/knarcraft/stargate/command/CommandReload.java index 6d29304..1bf6326 100644 --- a/src/main/java/net/knarcraft/stargate/command/CommandReload.java +++ b/src/main/java/net/knarcraft/stargate/command/CommandReload.java @@ -1,6 +1,7 @@ package net.knarcraft.stargate.command; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.SGFormatBuilder; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -17,7 +18,7 @@ public class CommandReload implements CommandExecutor { @NotNull String[] args) { if (commandSender instanceof Player player) { if (!player.hasPermission("stargate.admin.reload")) { - Stargate.getMessageSender().sendErrorMessage(commandSender, "Permission Denied"); + new SGFormatBuilder("Permission Denied").error(commandSender); return true; } } diff --git a/src/main/java/net/knarcraft/stargate/command/CommandStarGate.java b/src/main/java/net/knarcraft/stargate/command/CommandStarGate.java index 2185b54..5a67be4 100644 --- a/src/main/java/net/knarcraft/stargate/command/CommandStarGate.java +++ b/src/main/java/net/knarcraft/stargate/command/CommandStarGate.java @@ -1,10 +1,11 @@ package net.knarcraft.stargate.command; -import net.knarcraft.stargate.Stargate; import net.md_5.bungee.api.ChatColor; +import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import java.util.Arrays; @@ -16,6 +17,11 @@ import java.util.Arrays; * the plugin itself, not commands for functions of the plugin.

*/ public class CommandStarGate implements CommandExecutor { + private final Plugin stargate; + + public CommandStarGate(Plugin stargate) { + this.stargate = stargate; + } @Override public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @@ -31,8 +37,8 @@ public class CommandStarGate implements CommandExecutor { } return false; } else { - commandSender.sendMessage(ChatColor.GOLD + "Stargate version " + - ChatColor.GREEN + Stargate.getPluginVersion()); + commandSender.sendMessage(ChatColor.GREEN + "Stargate version " + ChatColor.GOLD + stargate.getDescription().getVersion() + + ChatColor.GREEN + " running on " + ChatColor.GOLD + Bukkit.getServer().getVersion()); return true; } } diff --git a/src/main/java/net/knarcraft/stargate/command/ConfigTabCompleter.java b/src/main/java/net/knarcraft/stargate/command/ConfigTabCompleter.java index ff09aa9..c3784eb 100644 --- a/src/main/java/net/knarcraft/stargate/command/ConfigTabCompleter.java +++ b/src/main/java/net/knarcraft/stargate/command/ConfigTabCompleter.java @@ -61,25 +61,26 @@ public class ConfigTabCompleter implements TabCompleter { * @param typedText

The beginning of the typed text, for filtering matching results

* @return

Some or all of the valid values for the option

*/ - private List getPossibleOptionValues(ConfigOption selectedOption, String typedText) { + @Nullable + private List getPossibleOptionValues(@NotNull ConfigOption selectedOption, + @NotNull String typedText) { switch (selectedOption) { - case LANGUAGE: + case LANGUAGE -> { //Return available languages return filterMatchingStartsWith(languages, typedText); - case GATE_FOLDER: - case PORTAL_FOLDER: - case DEFAULT_GATE_NETWORK: + } + case GATE_FOLDER, PORTAL_FOLDER, DEFAULT_GATE_NETWORK -> { //Just return the default value as most values should be possible if (typedText.trim().isEmpty()) { - return putStringInList((String) selectedOption.getDefaultValue()); + return List.of((String) selectedOption.getDefaultValue()); } else { return new ArrayList<>(); } - case MAIN_SIGN_COLOR: - case HIGHLIGHT_SIGN_COLOR: - case FREE_GATES_COLOR: + } + case MAIN_SIGN_COLOR, HIGHLIGHT_SIGN_COLOR, FREE_GATES_COLOR -> { //Return all colors return filterMatchingStartsWith(chatColors, typedText); + } } //If the config value is a boolean, show the two boolean values @@ -114,11 +115,13 @@ public class ConfigTabCompleter implements TabCompleter { * @param args

The arguments given by the user

* @return

Some or all of the valid values for the option

*/ - private List getPossibleStringListOptionValues(ConfigOption selectedOption, String[] args) { + @NotNull + private List getPossibleStringListOptionValues(@NotNull ConfigOption selectedOption, + @NotNull String[] args) { if (selectedOption == ConfigOption.PER_SIGN_COLORS) { return getPerSignColorCompletion(args); } else { - return null; + return new ArrayList<>(); } } @@ -128,7 +131,8 @@ public class ConfigTabCompleter implements TabCompleter { * @param args

The arguments given by the user

* @return

The options to give the user

*/ - private List getPerSignColorCompletion(String[] args) { + @NotNull + private List getPerSignColorCompletion(@NotNull String[] args) { if (args.length < 3) { return filterMatchingStartsWith(signTypes, args[1]); } else if (args.length < 4) { @@ -139,18 +143,6 @@ public class ConfigTabCompleter implements TabCompleter { return new ArrayList<>(); } - /** - * Puts a single string value into a string list - * - * @param value

The string to make into a list

- * @return

A list containing the string value

- */ - private List putStringInList(String value) { - List list = new ArrayList<>(); - list.add(value); - return list; - } - /** * Initializes all lists of auto-completable values */ @@ -200,6 +192,7 @@ public class ConfigTabCompleter implements TabCompleter { * * @return

The available chat colors

*/ + @NotNull private List getChatColors() { List chatColors = new ArrayList<>(); char[] colors = new char[]{'a', 'b', 'c', 'd', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; @@ -216,6 +209,7 @@ public class ConfigTabCompleter implements TabCompleter { */ private void initializeLanguages() { languages = new ArrayList<>(); + languages.add("cs"); languages.add("de"); languages.add("en"); languages.add("es"); @@ -223,12 +217,15 @@ public class ConfigTabCompleter implements TabCompleter { languages.add("hu"); languages.add("it"); languages.add("ja"); - languages.add("nb-no"); + languages.add("nb"); languages.add("nl"); - languages.add("nn-no"); - languages.add("pt-br"); + languages.add("nn"); + languages.add("pt"); languages.add("ru"); - languages.add("zh_cn"); + languages.add("sv"); + languages.add("tr"); + languages.add("uk"); + languages.add("zh"); //TODO: Generate this list dynamically by listing the language files in the jar and adding the user's custom // language files } diff --git a/src/main/java/net/knarcraft/stargate/command/StarGateTabCompleter.java b/src/main/java/net/knarcraft/stargate/command/StarGateTabCompleter.java index 96ecd3d..25c1d1d 100644 --- a/src/main/java/net/knarcraft/stargate/command/StarGateTabCompleter.java +++ b/src/main/java/net/knarcraft/stargate/command/StarGateTabCompleter.java @@ -17,8 +17,9 @@ import java.util.List; public class StarGateTabCompleter implements TabCompleter { @Override - public @Nullable List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, - @NotNull String s, @NotNull String[] args) { + @Nullable + public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, + @NotNull String[] args) { if (args.length == 1) { List commands = getAvailableCommands(commandSender); List matchingCommands = new ArrayList<>(); @@ -42,7 +43,8 @@ public class StarGateTabCompleter implements TabCompleter { * @param commandSender

The command sender to get available commands for

* @return

The commands available to the command sender

*/ - private List getAvailableCommands(CommandSender commandSender) { + @NotNull + private List getAvailableCommands(@NotNull CommandSender commandSender) { List commands = new ArrayList<>(); commands.add("about"); if (!(commandSender instanceof Player player) || player.hasPermission("stargate.admin.reload")) { diff --git a/src/main/java/net/knarcraft/stargate/config/ConfigOption.java b/src/main/java/net/knarcraft/stargate/config/ConfigOption.java index e12ea59..cdd8563 100644 --- a/src/main/java/net/knarcraft/stargate/config/ConfigOption.java +++ b/src/main/java/net/knarcraft/stargate/config/ConfigOption.java @@ -1,5 +1,8 @@ package net.knarcraft.stargate.config; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + /** * A ConfigOption represents one of the available config options */ @@ -55,7 +58,8 @@ public enum ConfigOption { */ PER_SIGN_COLORS("gates.cosmetic.perSignColors", "The per-sign color specification", new String[]{ "'ACACIA:default,default'", "'BIRCH:default,default'", "'CRIMSON:inverted,inverted'", "'DARK_OAK:inverted,inverted'", - "'JUNGLE:default,default'", "'OAK:default,default'", "'SPRUCE:inverted,inverted'", "'WARPED:inverted,inverted'"}), + "'JUNGLE:default,default'", "'OAK:default,default'", "'SPRUCE:inverted,inverted'", "'WARPED:inverted,inverted'", + "'BAMBOO:default,default'", "'CHERRY:default,default'"}), /** * Whether to destroy portals when any blocks are broken by explosions @@ -150,6 +154,11 @@ public enum ConfigOption { CHARGE_FREE_DESTINATION("economy.chargeFreeDestination", "Whether to require payment if the destination is free, but the entrance stargate is not", true), + /** + * The account to transfer all paid fees to + */ + TAX_ACCOUNT("economy.taxAccount", "The UUID of the account all fees are paid to (except for money to the Stargate owner)", ""), + /** * Whether to mark free gates with a different color */ @@ -189,7 +198,14 @@ public enum ConfigOption { * Whether to hide Dynmap icons by default */ DYNMAP_ICONS_DEFAULT_HIDDEN("dynmap.dynmapIconsHiddenByDefault", - "Whether to hide Stargate's Dynmap icons by default, requiring the user to enable them.", true); + "Whether to hide Stargate's Dynmap icons by default, requiring the user to enable them.", true), + + /** + * The amount of ticks to wait when processing the Stargate control update queue + */ + CONTROL_UPDATE_QUEUE_DELAY("gates.integrity.controlUpdateDelay", + "The delay between each time a Stargate's controls are updated after startup", 3), + ; private final String configNode; private final String description; @@ -203,7 +219,7 @@ public enum ConfigOption { * @param description

The description of what this config option does

* @param defaultValue

The default value of this config option

*/ - ConfigOption(String configNode, String description, Object defaultValue) { + ConfigOption(@NotNull String configNode, @NotNull String description, @NotNull Object defaultValue) { this.configNode = configNode; this.description = description; this.defaultValue = defaultValue; @@ -229,7 +245,7 @@ public enum ConfigOption { * @param name

The name of the config option to get

* @return

The corresponding config option, or null if the name is invalid

*/ - public static ConfigOption getByName(String name) { + public static @Nullable ConfigOption getByName(@NotNull String name) { for (ConfigOption option : ConfigOption.values()) { if (option.getName().equalsIgnoreCase(name)) { return option; @@ -243,7 +259,7 @@ public enum ConfigOption { * * @return

The name of this config option

*/ - public String getName() { + public @NotNull String getName() { if (!this.configNode.contains(".")) { return this.configNode; } @@ -256,7 +272,7 @@ public enum ConfigOption { * * @return

The data type used

*/ - public OptionDataType getDataType() { + public @NotNull OptionDataType getDataType() { return this.dataType; } @@ -265,7 +281,7 @@ public enum ConfigOption { * * @return

This config option's config node

*/ - public String getConfigNode() { + public @NotNull String getConfigNode() { return this.configNode; } @@ -274,7 +290,7 @@ public enum ConfigOption { * * @return

The description of this config option

*/ - public String getDescription() { + public @NotNull String getDescription() { return this.description; } @@ -283,7 +299,7 @@ public enum ConfigOption { * * @return

This config option's default value

*/ - public Object getDefaultValue() { + public @NotNull Object getDefaultValue() { return this.defaultValue; } diff --git a/src/main/java/net/knarcraft/stargate/config/ConfigTag.java b/src/main/java/net/knarcraft/stargate/config/ConfigTag.java index 024b205..a992d46 100644 --- a/src/main/java/net/knarcraft/stargate/config/ConfigTag.java +++ b/src/main/java/net/knarcraft/stargate/config/ConfigTag.java @@ -1,25 +1,38 @@ package net.knarcraft.stargate.config; -import java.util.Arrays; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; /** * A config tag groups config values by a property */ public enum ConfigTag { - COLOR(new ConfigOption[]{ConfigOption.FREE_GATES_COLOR, ConfigOption.MAIN_SIGN_COLOR, - ConfigOption.HIGHLIGHT_SIGN_COLOR, ConfigOption.PER_SIGN_COLORS}), - FOLDER(new ConfigOption[]{ConfigOption.GATE_FOLDER, ConfigOption.PORTAL_FOLDER}), - DYNMAP(new ConfigOption[]{ConfigOption.ENABLE_DYNMAP, ConfigOption.DYNMAP_ICONS_DEFAULT_HIDDEN}); + /** + * Color-related configuration options + */ + COLOR(Set.of(ConfigOption.FREE_GATES_COLOR, ConfigOption.MAIN_SIGN_COLOR, ConfigOption.HIGHLIGHT_SIGN_COLOR, + ConfigOption.PER_SIGN_COLORS)), - private final ConfigOption[] taggedOptions; + /** + * Folder-altering configuration options + */ + FOLDER(Set.of(ConfigOption.GATE_FOLDER, ConfigOption.PORTAL_FOLDER)), + + /** + * Dynmap-related configuration options + */ + DYNMAP(Set.of(ConfigOption.ENABLE_DYNMAP, ConfigOption.DYNMAP_ICONS_DEFAULT_HIDDEN)); + + private final Set taggedOptions; /** * Instantiates a new config tag * * @param taggedOptions

The config options included in this tag

*/ - ConfigTag(ConfigOption[] taggedOptions) { + ConfigTag(@NotNull Set taggedOptions) { this.taggedOptions = taggedOptions; } @@ -29,8 +42,8 @@ public enum ConfigTag { * @param option

The config option to check

* @return

True of the config option is tagged

*/ - public boolean isTagged(ConfigOption option) { - return Arrays.stream(taggedOptions).anyMatch((item) -> item == option); + public boolean isTagged(@NotNull ConfigOption option) { + return taggedOptions.contains(option); } /** @@ -39,7 +52,7 @@ public enum ConfigTag { * @param configOption

The config option to check

* @return

True if changing the config option requires a "reload of colors" to take effect

*/ - public static boolean requiresColorReload(ConfigOption configOption) { + public static boolean requiresColorReload(@NotNull ConfigOption configOption) { return (COLOR.isTagged(configOption) && configOption != ConfigOption.FREE_GATES_COLOR); } @@ -49,7 +62,7 @@ public enum ConfigTag { * @param option

The config option to check

* @return

True if changing the config option requires a full reload to take effect

*/ - public static boolean requiresFullReload(ConfigOption option) { + public static boolean requiresFullReload(@NotNull ConfigOption option) { return FOLDER.isTagged(option); } @@ -59,7 +72,7 @@ public enum ConfigTag { * @param configOption

The config option to check

* @return

True if changing the config option requires a reload of all dynmap markers

*/ - public static boolean requiresDynmapReload(ConfigOption configOption) { + public static boolean requiresDynmapReload(@NotNull ConfigOption configOption) { return DYNMAP.isTagged(configOption); } @@ -69,7 +82,7 @@ public enum ConfigTag { * @param option

The config option to check

* @return

True if changing the config option requires a portal reload to take effect

*/ - public static boolean requiresPortalReload(ConfigOption option) { + public static boolean requiresPortalReload(@NotNull ConfigOption option) { return COLOR.isTagged(option) || FOLDER.isTagged(option) || option == ConfigOption.VERIFY_PORTALS; } @@ -79,7 +92,7 @@ public enum ConfigTag { * @param option

The config option to check

* @return

True if the language loader requires a reload

*/ - public static boolean requiresLanguageReload(ConfigOption option) { + public static boolean requiresLanguageReload(@NotNull ConfigOption option) { return option == ConfigOption.LANGUAGE; } @@ -89,7 +102,7 @@ public enum ConfigTag { * @param option

The config option to check

* @return

True if economy requires a reload

*/ - public static boolean requiresEconomyReload(ConfigOption option) { + public static boolean requiresEconomyReload(@NotNull ConfigOption option) { return option == ConfigOption.USE_ECONOMY; } diff --git a/src/main/java/net/knarcraft/stargate/config/DynmapManager.java b/src/main/java/net/knarcraft/stargate/config/DynmapManager.java index 5eaa0c9..8283e5a 100644 --- a/src/main/java/net/knarcraft/stargate/config/DynmapManager.java +++ b/src/main/java/net/knarcraft/stargate/config/DynmapManager.java @@ -1,6 +1,7 @@ package net.knarcraft.stargate.config; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalRegistry; import org.bukkit.Location; @@ -10,6 +11,8 @@ import org.dynmap.markers.GenericMarker; import org.dynmap.markers.Marker; import org.dynmap.markers.MarkerIcon; import org.dynmap.markers.MarkerSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * A manager for dealing with everything Dynmap @@ -27,9 +30,10 @@ public final class DynmapManager { * Initializes the dynmap manager * * @param dynmapAPI

A reference

+ * @throws NullPointerException

If dynmap has an invalid state

*/ - public static void initialize(DynmapAPI dynmapAPI) { - if (dynmapAPI == null || dynmapAPI.getMarkerAPI() == null) { + public static void initialize(@Nullable DynmapAPI dynmapAPI) throws NullPointerException { + if (dynmapAPI == null || !dynmapAPI.markerAPIInitialized() || dynmapAPI.getMarkerAPI() == null) { markerSet = null; portalIcon = null; } else { @@ -66,7 +70,7 @@ public final class DynmapManager { * * @param portal

The portal to add a marker for

*/ - public static void addPortalMarker(Portal portal) { + public static void addPortalMarker(@NotNull Portal portal) { if (markerSet == null || Stargate.getStargateConfig().isDynmapDisabled()) { return; } @@ -75,8 +79,22 @@ public final class DynmapManager { return; } - Location location = portal.getBlockAt(portal.getGate().getLayout().getExit()); - Marker marker = markerSet.createMarker(getPortalMarkerId(portal), portal.getName(), world.getName(), + Location location; + @Nullable RelativeBlockVector exit = portal.getGate().getLayout().getExit(); + if (exit == null) { + location = portal.getTopLeft(); + } else { + location = portal.getBlockAt(exit); + } + + String markerId = getPortalMarkerId(portal); + if (markerSet.findMarker(markerId) != null) { + Stargate.debug("DynmapManager::addPortalMarker", "Skipped marker creation, as the portal " + + "marker " + markerId + " already exists"); + return; + } + + Marker marker = markerSet.createMarker(markerId, portal.getName(), world.getName(), location.getX(), location.getY(), location.getZ(), portalIcon, false); if (marker == null) { Stargate.logWarning(String.format( @@ -86,7 +104,7 @@ public final class DynmapManager { Portal name: %s Portal world: %s Portal location: %s,%s,%s""", - getPortalMarkerId(portal), portal.getName(), world.getName(), location.getX(), location.getY(), + markerId, portal.getName(), world.getName(), location.getX(), location.getY(), location.getZ())); return; } @@ -111,7 +129,7 @@ public final class DynmapManager { * * @param portal

The portal to remove the marker for

*/ - public static void removePortalMarker(Portal portal) { + public static void removePortalMarker(@NotNull Portal portal) { if (markerSet == null || Stargate.getStargateConfig().isDynmapDisabled()) { return; } @@ -121,13 +139,21 @@ public final class DynmapManager { } } + /** + * Disables Dynmap integration + */ + public static void disable() { + markerSet = null; + portalIcon = null; + } + /** * Gets the id used for the given portal's marker * * @param portal

The portal to get a marker id for

* @return

*/ - private static String getPortalMarkerId(Portal portal) { + private static String getPortalMarkerId(@NotNull Portal portal) { return portal.getNetwork() + "-:-" + portal.getName(); } diff --git a/src/main/java/net/knarcraft/stargate/config/EconomyConfig.java b/src/main/java/net/knarcraft/stargate/config/EconomyConfig.java index 1b8400c..165cf51 100644 --- a/src/main/java/net/knarcraft/stargate/config/EconomyConfig.java +++ b/src/main/java/net/knarcraft/stargate/config/EconomyConfig.java @@ -11,6 +11,8 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.ServicesManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Map; @@ -29,7 +31,7 @@ public final class EconomyConfig { * * @param configOptions

The loaded config options to read

*/ - public EconomyConfig(Map configOptions) { + public EconomyConfig(@NotNull Map configOptions) { this.configOptions = configOptions; try { String freeColor = (String) configOptions.get(ConfigOption.FREE_GATES_COLOR); @@ -62,6 +64,7 @@ public final class EconomyConfig { * * @return

An economy object, or null if economy is disabled or not initialized

*/ + @Nullable public Economy getEconomy() { return economy; } @@ -71,6 +74,7 @@ public final class EconomyConfig { * * @return

An instance of the Vault plugin, or null if Vault is not loaded

*/ + @Nullable public Plugin getVault() { return vault; } @@ -132,6 +136,16 @@ public final class EconomyConfig { return (Integer) configOptions.get(ConfigOption.DESTROY_COST); } + /** + * Gets the account all taxes are paid to + * + * @return

The account all taxes are paid to

+ */ + @Nullable + public String getTaxAccount() { + return (String) configOptions.get(ConfigOption.TAX_ACCOUNT); + } + /** * Checks whether the given player can afford the given fee * @@ -149,6 +163,7 @@ public final class EconomyConfig { * @param amount

The amount to display

* @return

A formatted text string describing the amount

*/ + @NotNull public String format(int amount) { if (isEconomyEnabled()) { return economy.format(amount); @@ -163,7 +178,7 @@ public final class EconomyConfig { * @param pluginManager

The plugin manager to get plugins from

* @return

True if economy was enabled

*/ - public boolean setupEconomy(PluginManager pluginManager) { + public boolean setupEconomy(@NotNull PluginManager pluginManager) { if (!isEconomyEnabled()) { return false; } @@ -177,10 +192,10 @@ public final class EconomyConfig { this.vault = vault; return true; } else { - Stargate.logInfo(Stargate.getString("ecoLoadError")); + Stargate.logInfo(new SGFormatBuilder(Message.ECONOMY_LOAD_ERROR).toString()); } } else { - Stargate.logInfo(Stargate.getString("vaultLoadError")); + Stargate.logInfo(new SGFormatBuilder(Message.VAULT_LOAD_ERROR).toString()); } configOptions.put(ConfigOption.USE_ECONOMY, false); return false; @@ -202,7 +217,7 @@ public final class EconomyConfig { * @param gate

The gate type used

* @return

The cost of creating the gate

*/ - public int getCreateCost(Player player, Gate gate) { + public int getCreateCost(@NotNull Player player, @NotNull Gate gate) { if (isFree(player, "create")) { return 0; } else { @@ -217,7 +232,7 @@ public final class EconomyConfig { * @param gate

The gate type used

* @return

The cost of destroying the gate

*/ - public int getDestroyCost(Player player, Gate gate) { + public int getDestroyCost(@NotNull Player player, @NotNull Gate gate) { if (isFree(player, "destroy")) { return 0; } else { @@ -232,7 +247,7 @@ public final class EconomyConfig { * @param permissionNode

The free.permissionNode necessary to allow free gate {action}

* @return

*/ - private boolean isFree(Player player, String permissionNode) { + private boolean isFree(@NotNull Player player, @NotNull String permissionNode) { return !useEconomy() || PermissionHelper.hasPermission(player, "stargate.free." + permissionNode); } diff --git a/src/main/java/net/knarcraft/stargate/config/LanguageLoader.java b/src/main/java/net/knarcraft/stargate/config/LanguageLoader.java index 0383794..f36b5c6 100644 --- a/src/main/java/net/knarcraft/stargate/config/LanguageLoader.java +++ b/src/main/java/net/knarcraft/stargate/config/LanguageLoader.java @@ -3,15 +3,16 @@ package net.knarcraft.stargate.config; import net.knarcraft.knarlib.property.ColorConversion; import net.knarcraft.knarlib.util.FileHelper; import net.knarcraft.stargate.Stargate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; -import java.util.Set; /** * This class is responsible for loading all strings which are translated into several languages @@ -19,9 +20,9 @@ import java.util.Set; public final class LanguageLoader { private final String languageFolder; - private final Map loadedBackupStrings; + private final Map loadedBackupStrings; private String chosenLanguage; - private Map loadedStringTranslations; + private Map loadedStringTranslations; /** * Instantiates a new language loader @@ -30,7 +31,7 @@ public final class LanguageLoader { * * @param languageFolder

The folder containing the language files

*/ - public LanguageLoader(String languageFolder) { + public LanguageLoader(@NotNull String languageFolder) { this.languageFolder = languageFolder; File testFile = new File(languageFolder, "en.txt"); if (!testFile.exists()) { @@ -45,7 +46,7 @@ public final class LanguageLoader { loadedBackupStrings = load("en", inputStream); } else { loadedBackupStrings = null; - Stargate.getConsoleLogger().severe("[stargate] Error loading backup language. " + + Stargate.logSevere("Error loading backup language. " + "There may be missing text in-game"); } } @@ -60,32 +61,34 @@ public final class LanguageLoader { } /** - * Gets the string to display given its name/key + * Gets the string to display given its message key * - * @param name

The name/key of the string to display

- * @return

The string in the user's preferred language

+ * @param message

The message to display

+ * @return

The message in the user's preferred language

*/ - public String getString(String name) { + @NotNull + public String getString(@NotNull Message message) { String value = null; if (loadedStringTranslations != null) { - value = loadedStringTranslations.get(name); + value = loadedStringTranslations.get(message); } if (value == null) { - value = getBackupString(name); + value = getBackupString(message); } return value; } /** - * Gets the string to display given its name/key + * Gets the string to display given its message key * - * @param name

The name/key of the string to display

+ * @param message

The message to display

* @return

The string in the backup language (English)

*/ - public String getBackupString(String name) { + @NotNull + public String getBackupString(@NotNull Message message) { String value = null; if (loadedBackupStrings != null) { - value = loadedBackupStrings.get(name); + value = loadedBackupStrings.get(message); } if (value == null) { return ""; @@ -98,7 +101,7 @@ public final class LanguageLoader { * * @param chosenLanguage

The new plugin language

*/ - public void setChosenLanguage(String chosenLanguage) { + public void setChosenLanguage(@NotNull String chosenLanguage) { this.chosenLanguage = chosenLanguage; } @@ -107,21 +110,21 @@ public final class LanguageLoader { * * @param language

The language to update

*/ - private void updateLanguage(String language) { - Map currentLanguageValues = load(language); + private void updateLanguage(@NotNull String language) { + Map currentLanguageValues = load(language); - InputStream inputStream = getClass().getResourceAsStream("/lang/" + language + ".txt"); + InputStream inputStream = FileHelper.getInputStreamForInternalFile("/lang/" + language + ".txt"); if (inputStream == null) { - Stargate.logInfo(String.format("The language %s is not available. Falling back to english, You can add a " + - "custom language by creating a new text file in the lang directory.", language)); + Stargate.logInfo(String.format("Unable to find internal language file for %s. This will normally not " + + "cause any problems, except newly added translatable strings won't be automatically added", language)); Stargate.debug("LanguageLoader::updateLanguage", String.format("Unable to load /lang/%s.txt", language)); return; } try { readChangedLanguageStrings(inputStream, language, currentLanguageValues); - } catch (IOException ex) { - ex.printStackTrace(); + } catch (IOException exception) { + Stargate.logSevere("Unable to read language strings! Message: " + exception.getMessage()); } } @@ -133,12 +136,12 @@ public final class LanguageLoader { * @param currentLanguageValues

The current values of the loaded/processed language

* @throws IOException

if unable to read a language file

*/ - private void readChangedLanguageStrings(InputStream inputStream, String language, Map currentLanguageValues) throws IOException { + private void readChangedLanguageStrings(@NotNull InputStream inputStream, @NotNull String language, + @Nullable Map currentLanguageValues) throws IOException { //Get language values BufferedReader bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream); - Map internalLanguageValues = FileHelper.readKeyValuePairs(bufferedReader, "=", - ColorConversion.NORMAL); + Map internalLanguageValues = fromStringMap(FileHelper.readKeyValuePairs(bufferedReader, + "=", ColorConversion.NORMAL)); //If currentLanguageValues is null; the chosen language has not been used before if (currentLanguageValues == null) { @@ -149,11 +152,14 @@ public final class LanguageLoader { //If a key is not found in the language file, add the one in the internal file. Must update the external file if (!internalLanguageValues.keySet().equals(currentLanguageValues.keySet())) { - Map newLanguageValues = new HashMap<>(); + Map newLanguageValues = new EnumMap<>(Message.class); boolean updateNecessary = false; - for (String key : internalLanguageValues.keySet()) { + for (Map.Entry entry : internalLanguageValues.entrySet()) { + Message key = entry.getKey(); + String value = entry.getValue(); + if (currentLanguageValues.get(key) == null) { - newLanguageValues.put(key, internalLanguageValues.get(key)); + newLanguageValues.put(key, value); //Found at least one value in the internal file not in the external file. Need to update updateNecessary = true; } else { @@ -177,20 +183,20 @@ public final class LanguageLoader { * @param customLanguageStrings

Any custom language strings not recognized

* @throws IOException

If unable to write to the language file

*/ - private void updateLanguageFile(String language, Map languageStrings, - Map customLanguageStrings) throws IOException { + private void updateLanguageFile(@NotNull String language, @NotNull Map languageStrings, + @Nullable Map customLanguageStrings) throws IOException { BufferedWriter bufferedWriter = FileHelper.getBufferedWriterFromString(languageFolder + language + ".txt"); //Output normal Language data - for (String key : languageStrings.keySet()) { - bufferedWriter.write(key + "=" + languageStrings.get(key)); + for (Map.Entry entry : languageStrings.entrySet()) { + bufferedWriter.write(entry.getKey() + "=" + entry.getValue()); bufferedWriter.newLine(); } bufferedWriter.newLine(); //Output any custom language strings the user had if (customLanguageStrings != null) { - for (String key : customLanguageStrings.keySet()) { - bufferedWriter.write(key + "=" + customLanguageStrings.get(key)); + for (Map.Entry entry : customLanguageStrings.entrySet()) { + bufferedWriter.write(entry.getKey() + "=" + entry.getValue()); bufferedWriter.newLine(); } } @@ -203,7 +209,8 @@ public final class LanguageLoader { * @param lang

The language to load

* @return

A mapping between loaded string indexes and the strings to display

*/ - private Map load(String lang) { + @Nullable + private Map load(@NotNull String lang) { return load(lang, null); } @@ -214,8 +221,8 @@ public final class LanguageLoader { * @param inputStream

An optional input stream to use. Defaults to using a file input stream

* @return

A mapping between loaded string indexes and the strings to display

*/ - private Map load(String lang, InputStream inputStream) { - Map strings; + @Nullable + private Map load(@NotNull String lang, @Nullable InputStream inputStream) { BufferedReader bufferedReader; try { if (inputStream == null) { @@ -223,14 +230,13 @@ public final class LanguageLoader { } else { bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream); } - strings = FileHelper.readKeyValuePairs(bufferedReader, "=", ColorConversion.NORMAL); - } catch (Exception e) { + return fromStringMap(FileHelper.readKeyValuePairs(bufferedReader, "=", ColorConversion.NORMAL)); + } catch (Exception exception) { if (Stargate.getStargateConfig().isDebuggingEnabled()) { - Stargate.getConsoleLogger().info("[Stargate] Unable to load language " + lang); + Stargate.logInfo("Unable to load language " + lang); } return null; } - return strings; } /** @@ -238,20 +244,41 @@ public final class LanguageLoader { */ public void debug() { if (loadedStringTranslations != null) { - Set keys = loadedStringTranslations.keySet(); - for (String key : keys) { - Stargate.debug("LanguageLoader::Debug::loadedStringTranslations", key + " => " + - loadedStringTranslations.get(key)); + for (Map.Entry entry : loadedStringTranslations.entrySet()) { + Stargate.debug("LanguageLoader::Debug::loadedStringTranslations", entry.getKey() + + " => " + entry.getValue()); } } if (loadedBackupStrings == null) { return; } - Set keys = loadedBackupStrings.keySet(); - for (String key : keys) { - Stargate.debug("LanguageLoader::Debug::loadedBackupStrings", key + " => " + - loadedBackupStrings.get(key)); + + for (Map.Entry entry : loadedBackupStrings.entrySet()) { + Stargate.debug("LanguageLoader::Debug::loadedBackupStrings", entry.getKey() + " => " + + entry.getValue()); } } + /** + * Converts a map from string key to message into a map from message key to message + * + * @param configurationStrings

The map to convert

+ * @return

The converted map

+ */ + @NotNull + private Map fromStringMap(@NotNull Map configurationStrings) { + Map output = new EnumMap<>(Message.class); + for (Map.Entry entry : configurationStrings.entrySet()) { + Message message = Message.getFromKey(entry.getKey()); + if (message == null) { + Stargate.logWarning("Found unrecognized language key " + entry.getKey()); + continue; + } + + output.put(message, entry.getValue()); + } + + return output; + } + } diff --git a/src/main/java/net/knarcraft/stargate/config/Message.java b/src/main/java/net/knarcraft/stargate/config/Message.java new file mode 100644 index 0000000..312f5ad --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/config/Message.java @@ -0,0 +1,251 @@ +package net.knarcraft.stargate.config; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Translated messages displayed to players + */ +public enum Message { + + /** + * The prefix displayed in front of all messages shown in the chat + */ + PREFIX("prefix"), + + /** + * The message displayed when a player is teleported + */ + TELEPORTED("teleportMsg"), + + /** + * The message displayed when a player destroys a Stargate + */ + DESTROYED("destroyMsg"), + + /** + * The message displayed when the currently selected Stargate destination is invalid + */ + INVALID_DESTINATION("invalidMsg"), + + /** + * The message displayed when the destination portal is busy with another player + */ + DESTINATION_BLOCKED("blockMsg"), + + /** + * The message displayed when the Stargate has no destinations available to the player + */ + NO_DESTINATION("destEmpty"), + + /** + * The message displayed when a player is denied access to any action + */ + ACCESS_DENIED("denyMsg"), + + /** + * The message displayed when the plugin is reloaded + */ + RELOADED("reloaded"), + + /** + * The message displayed when a player has some currency deducted from their account + */ + ECONOMY_DEDUCTED("ecoDeduct"), + + /** + * The message displayed when a player has some currency refunded to their account + */ + ECONOMY_REFUNDED("ecoRefund"), + + /** + * The message displayed when a player obtains some currency to their account (from portal usage) + */ + ECONOMY_OBTAINED("ecoObtain"), + + /** + * The message displayed when the player has an insufficient amount of currency to perform an action + */ + ECONOMY_INSUFFICIENT("ecoInFunds"), + + /** + * The message displayed when economy fails to load + */ + ECONOMY_LOAD_ERROR("ecoLoadError"), + + /** + * The message displayed when Vault fails to load + */ + VAULT_LOAD_ERROR("vaultLoadError"), + + /** + * The message displayed when Vault successfully loads + */ + VAULT_LOADED("vaultLoaded"), + + /** + * The message displayed when a Stargate is successfully created + */ + CREATED("createMsg"), + + /** + * The message displayed when a player is denied from creating a Stargate on the selected network + */ + CREATION_NETWORK_DENIED("createNetDeny"), + + /** + * The message displayed when a player is denied from creating a Stargate of the given gate type + */ + CREATION_GATE_DENIED("createGateDeny"), + + /** + * The message displayed when a Stargate is created on the player's personal network + */ + CREATION_PERSONAL("createPersonal"), + + /** + * The message displayed when the name of a Stargate is too short or too long + */ + CREATION_NAME_LENGTH("createNameLength"), + + /** + * The message displayed when another Stargate on the network has the same name as the new Stargate + */ + CREATION_NAME_COLLISION("createExists"), + + /** + * The message displayed when the specified network is full + */ + CREATION_NETWORK_FULL("createFull"), + + /** + * The message displayed when a player is denied from creating a Stargate in the current world + */ + CREATION_WORLD_DENIED("createWorldDeny"), + + /** + * The message displayed when a gate is physically conflicting with another + */ + CREATION_CONFLICT("createConflict"), + + /** + * The right-click prompt displayed on Stargate signs + */ + SIGN_RIGHT_CLICK("signRightClick"), + + /** + * The to use prompt displayed on Stargate signs + */ + SIGN_TO_USE("signToUse"), + + /** + * The random string displayed on Stargate signs + */ + SIGN_RANDOM("signRandom"), + + /** + * The disconnected string displayed on Stargate signs + */ + SIGN_DISCONNECTED("signDisconnected"), + + /** + * The invalid gate string displayed on Stargate signs + */ + SIGN_INVALID("signInvalidGate"), + + /** + * The message displayed if trying to create a bungee gate when bungee is disabled + */ + BUNGEE_DISABLED("bungeeDisabled"), + + /** + * The message displayed when a player is denied from creating a bungee Stargate + */ + BUNGEE_CREATION_DENIED("bungeeDeny"), + + /** + * The message displayed if a Stargate is missing the destination, the network or both + */ + BUNGEE_MISSING_INFO("bungeeEmpty"), + + /** + * The teleportation prompt shown on bungee signs + */ + BUNGEE_SIGN("bungeeSign"), + + /** + * The format of the title of the portal info shown in chat + */ + PORTAL_INFO_TITLE("portalInfoTitle"), + + /** + * The format of the name of the portal info shown in chat + */ + PORTAL_INFO_NAME("portalInfoName"), + + /** + * The format of the destination of the portal info shown in chat + */ + PORTAL_INFO_DESTINATION("portalInfoDestination"), + + /** + * The format of the network of the portal info shown in chat + */ + PORTAL_INFO_NETWORK("portalInfoNetwork"), + + /** + * The format of the server of the portal info shown in chat + */ + PORTAL_INFO_SERVER("portalInfoServer"), + + /** + * The author that created the loaded translation + */ + AUTHOR("author"), + ; + + private final String key; + + /** + * Instantiates a new message + * + * @param key

The key of the message in the language files

+ */ + Message(@NotNull String key) { + this.key = key; + } + + /** + * Gets the language file key for this message + * + * @return

This message's key

+ */ + @NotNull + public String getKey() { + return this.key; + } + + /** + * Gets the message corresponding to the given key + * + * @param key

The key to get a message from

+ * @return

The message, or null if not found

+ */ + @Nullable + public static Message getFromKey(@NotNull String key) { + for (Message message : Message.values()) { + if (message.getKey().equalsIgnoreCase(key)) { + return message; + } + } + + return null; + } + + @Override + @NotNull + public String toString() { + return this.getKey(); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/config/MessageSender.java b/src/main/java/net/knarcraft/stargate/config/MessageSender.java deleted file mode 100644 index bfc706a..0000000 --- a/src/main/java/net/knarcraft/stargate/config/MessageSender.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.knarcraft.stargate.config; - -import net.md_5.bungee.api.ChatColor; -import org.bukkit.command.CommandSender; - -/** - * The message sender is responsible sending messages to players with correct coloring and formatting - */ -public final class MessageSender { - - private final LanguageLoader languageLoader; - - /** - * Instantiates a new message sender - * - * @param languageLoader

The language loader to get translated strings from

- */ - public MessageSender(LanguageLoader languageLoader) { - this.languageLoader = languageLoader; - } - - /** - * Sends an error message to a player - * - * @param player

The player to send the message to

- * @param message

The message to send

- */ - public void sendErrorMessage(CommandSender player, String message) { - sendMessage(player, message, true); - } - - /** - * Sends a success message to a player - * - * @param player

The player to send the message to

- * @param message

The message to send

- */ - public void sendSuccessMessage(CommandSender player, String message) { - sendMessage(player, message, false); - } - - /** - * Sends a message to a player - * - * @param sender

The player to send the message to

- * @param message

The message to send

- * @param error

Whether the message sent is an error

- */ - private void sendMessage(CommandSender sender, String message, boolean error) { - if (message.isEmpty()) { - return; - } - message = ChatColor.translateAlternateColorCodes('&', message); - if (error) { - sender.sendMessage(ChatColor.RED + languageLoader.getString("prefix") + ChatColor.WHITE + message); - } else { - sender.sendMessage(ChatColor.GREEN + languageLoader.getString("prefix") + ChatColor.WHITE + message); - } - } - -} diff --git a/src/main/java/net/knarcraft/stargate/config/SGFormatBuilder.java b/src/main/java/net/knarcraft/stargate/config/SGFormatBuilder.java new file mode 100644 index 0000000..23b83fb --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/config/SGFormatBuilder.java @@ -0,0 +1,40 @@ +package net.knarcraft.stargate.config; + +import net.knarcraft.knarlib.formatting.FormatBuilder; +import net.knarcraft.stargate.Stargate; +import org.jetbrains.annotations.NotNull; + +/** + * A customized format builder for automatic translation of Stargate messages + */ +public class SGFormatBuilder extends FormatBuilder { + + /** + * Instantiates a new format builder + */ + public SGFormatBuilder() { + super(); + } + + /** + * Instantiates a new format builder + * + *

If the input is a list, it will be joined using the default delimiter: ",".

+ * + * @param input

The input to use as the initial string of this format builder

+ * @throws IllegalStateException

If the string formatter has not been set, and the input is a translatable message

+ */ + public SGFormatBuilder(@NotNull K input) throws IllegalStateException { + super(input); + } + + @Override + @NotNull + protected String asString(@NotNull K input, @NotNull String delimiter) { + if (input instanceof Message message) { + return Stargate.getStargateConfig().getLanguageLoader().getString(message); + } + return super.asString(input, delimiter); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/config/StargateConfig.java b/src/main/java/net/knarcraft/stargate/config/StargateConfig.java index f03b852..afb81ff 100644 --- a/src/main/java/net/knarcraft/stargate/config/StargateConfig.java +++ b/src/main/java/net/knarcraft/stargate/config/StargateConfig.java @@ -1,7 +1,9 @@ package net.knarcraft.stargate.config; +import net.knarcraft.knarlib.formatting.StringFormatter; +import net.knarcraft.knarlib.formatting.Translator; import net.knarcraft.knarlib.property.ColorConversion; -import net.knarcraft.knarlib.util.FileHelper; +import net.knarcraft.knarlib.util.ConfigHelper; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.listener.BungeeCordListener; @@ -17,9 +19,9 @@ import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.messaging.Messenger; import org.dynmap.DynmapAPI; +import org.jetbrains.annotations.NotNull; import java.io.File; -import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -27,6 +29,8 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * The stargate config is responsible for keeping track of all configuration values @@ -38,7 +42,6 @@ public final class StargateConfig { private final HashSet managedWorlds = new HashSet<>(); private StargateGateConfig stargateGateConfig; - private MessageSender messageSender; private final LanguageLoader languageLoader; private EconomyConfig economyConfig; private final Logger logger; @@ -47,6 +50,7 @@ public final class StargateConfig { private String gateFolder; private String portalFolder; private String languageName = "en"; + private boolean isLoaded = false; private final Map configOptions; @@ -55,7 +59,7 @@ public final class StargateConfig { * * @param logger

The logger to use for logging errors

*/ - public StargateConfig(Logger logger) { + public StargateConfig(@NotNull Logger logger) { this.logger = logger; configOptions = new HashMap<>(); @@ -73,6 +77,7 @@ public final class StargateConfig { * * @return

A reference to the config options map

*/ + @NotNull public Map getConfigOptionsReference() { return configOptions; } @@ -92,22 +97,48 @@ public final class StargateConfig { languageLoader.setChosenLanguage(languageName); languageLoader.reload(); - messageSender = new MessageSender(languageLoader); + // Update prefix of the format builder + SGFormatBuilder.setStringFormatter(getStringFormatter()); + if (isDebuggingEnabled()) { languageLoader.debug(); } - this.createMissingFolders(); this.loadGates(); + this.createMissingFolders(); this.loadAllPortals(); //Set up vault economy if vault has been loaded setupVaultEconomy(); - DynmapAPI dynmapAPI = (DynmapAPI) Bukkit.getPluginManager().getPlugin("dynmap"); - if (dynmapAPI != null) { - DynmapManager.initialize(dynmapAPI); - DynmapManager.addAllPortalMarkers(); + + //Set up dynmap + try { + DynmapAPI dynmapAPI = (DynmapAPI) Bukkit.getPluginManager().getPlugin("dynmap"); + if (dynmapAPI != null) { + try { + DynmapManager.initialize(dynmapAPI); + DynmapManager.addAllPortalMarkers(); + } catch (NullPointerException ignored) { + logger.warning("Dynmap started in an invalid state. Check your log/console for dynmap-related " + + "problems. Dynmap integration cannot be initialized."); + } + } + } catch (NoClassDefFoundError error) { + logger.warning("Dynmap seems to be unavailable, even though its API is registered. Dynmap " + + "integration is disabled."); + DynmapManager.disable(); } + + this.isLoaded = true; + } + + /** + * Gets whether this configuration has been fully loaded + * + * @return

True if not fully loaded

+ */ + public boolean isNotLoaded() { + return !this.isLoaded; } /** @@ -115,6 +146,7 @@ public final class StargateConfig { * * @return

The loaded config options

*/ + @NotNull public Map getConfigOptions() { return new HashMap<>(configOptions); } @@ -126,6 +158,7 @@ public final class StargateConfig { * * @return

The open portals queue

*/ + @NotNull public Queue getOpenPortalsQueue() { return openPortalsQueue; } @@ -137,6 +170,7 @@ public final class StargateConfig { * * @return

The active portals queue

*/ + @NotNull public Queue getActivePortalsQueue() { return activePortalsQueue; } @@ -182,6 +216,7 @@ public final class StargateConfig { * * @return

The object containing economy config values

*/ + @NotNull public EconomyConfig getEconomyConfig() { return this.economyConfig; } @@ -191,16 +226,16 @@ public final class StargateConfig { * * @param sender

The sender of the reload request

*/ - public void reload(CommandSender sender) { + public void reload(@NotNull CommandSender sender) { //Unload all saved data unload(); //Perform all block change requests to prevent mismatch if a gate's open-material changes. Changing the // closed-material still requires a restart. - BlockChangeRequest firstElement = Stargate.getBlockChangeRequestQueue().peek(); + BlockChangeRequest firstElement = Stargate.getControlBlockUpdateRequestQueue().peek(); while (firstElement != null) { BlockChangeThread.pollQueue(); - firstElement = Stargate.getBlockChangeRequestQueue().peek(); + firstElement = Stargate.getControlBlockUpdateRequestQueue().peek(); } //Store the old enable bungee state in case it changes @@ -217,7 +252,10 @@ public final class StargateConfig { //Reload portal markers DynmapManager.addAllPortalMarkers(); - messageSender.sendErrorMessage(sender, languageLoader.getString("reloaded")); + // Update prefix of the format builder + SGFormatBuilder.setStringFormatter(getStringFormatter()); + + new SGFormatBuilder(Message.RELOADED).error(sender); } /** @@ -264,6 +302,7 @@ public final class StargateConfig { * * @return

The managed worlds

*/ + @NotNull public Set getManagedWorlds() { return new HashSet<>(managedWorlds); } @@ -273,7 +312,7 @@ public final class StargateConfig { * * @param worldName

The name of the world to manage

*/ - public void addManagedWorld(String worldName) { + public void addManagedWorld(@NotNull String worldName) { managedWorlds.add(worldName); } @@ -282,7 +321,7 @@ public final class StargateConfig { * * @param worldName

The name of the world to stop managing

*/ - public void removeManagedWorld(String worldName) { + public void removeManagedWorld(@NotNull String worldName) { managedWorlds.remove(worldName); } @@ -364,9 +403,11 @@ public final class StargateConfig { FileConfiguration newConfig = Stargate.getInstance().getConfig(); boolean isMigrating = false; - if (newConfig.getString("lang") != null || newConfig.getString("economy.freeGatesGreen") != null) { - migrateConfig(newConfig); + if (newConfig.getString("lang") != null || newConfig.getString("economy.taxAccount") == null) { + ConfigHelper.migrateConfig(Stargate.getInstance()); isMigrating = true; + Stargate.getInstance().reloadConfig(); + newConfig = Stargate.getInstance().getConfig(); } //Copy missing default values if any values are missing @@ -380,13 +421,10 @@ public final class StargateConfig { //Load the option using its correct data type switch (option.getDataType()) { case STRING_LIST -> optionValue = newConfig.getStringList(configNode); - case STRING -> { - String value = newConfig.getString(configNode); - optionValue = value != null ? value.trim() : ""; - } - case BOOLEAN -> optionValue = newConfig.getBoolean(configNode); - case INTEGER -> optionValue = newConfig.getInt(configNode); - case DOUBLE -> optionValue = newConfig.getDouble(configNode); + case STRING -> optionValue = newConfig.getString(configNode, (String) option.getDefaultValue()).trim(); + case BOOLEAN -> optionValue = newConfig.getBoolean(configNode, (boolean) option.getDefaultValue()); + case INTEGER -> optionValue = newConfig.getInt(configNode, (int) option.getDefaultValue()); + case DOUBLE -> optionValue = newConfig.getDouble(configNode, (double) option.getDefaultValue()); default -> throw new IllegalArgumentException("Invalid config data type encountered"); } configOptions.put(option, optionValue); @@ -397,10 +435,24 @@ public final class StargateConfig { //Get important folders from the config portalFolder = (String) configOptions.get(ConfigOption.PORTAL_FOLDER); + if (portalFolder.isEmpty()) { + portalFolder = dataFolderPath + "/portals/"; + } else { + portalFolder = replacePluginFolderPath(portalFolder); + } + Stargate.debug("StargateConfig::loadConfig", "Portal folder is " + portalFolder); + gateFolder = (String) configOptions.get(ConfigOption.GATE_FOLDER); + if (gateFolder.isEmpty()) { + gateFolder = dataFolderPath + "/gates/"; + } else { + gateFolder = replacePluginFolderPath(gateFolder); + } + Stargate.debug("StargateConfig::loadConfig", "Gate folder is " + gateFolder); //If users have an outdated config, assume they also need to update their default gates if (isMigrating) { + this.createMissingFolders(); GateHandler.writeDefaultGatesToFolder(gateFolder); } @@ -413,11 +465,29 @@ public final class StargateConfig { Stargate.getInstance().saveConfig(); } + /** + * Replaces "plugins/Stargate" in a folder path, and replaces it with the full path relative to the data folder + * + * @param input

The input string to replace in

+ * @return

The replaced path, or the input if not applicable

+ */ + @NotNull + private String replacePluginFolderPath(@NotNull String input) { + Pattern pattern = Pattern.compile("(?i)^plugins[\\\\/]Stargate"); + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + return dataFolderPath + matcher.replaceAll(""); + } else { + return input; + } + } + /** * Gets the object containing configuration values regarding gates * * @return

Gets the gate config

*/ + @NotNull public StargateGateConfig getStargateGateConfig() { return stargateGateConfig; } @@ -430,54 +500,15 @@ public final class StargateConfig { Stargate.logInfo(String.format("Loaded %s gate layouts", GateHandler.getGateCount())); } - /** - * Changes all configuration values from the old name to the new name - * - * @param newConfig

The config to read from and write to

- */ - private void migrateConfig(FileConfiguration newConfig) { - //Save the old config just in case something goes wrong - try { - newConfig.save(dataFolderPath + "/config.yml.old"); - } catch (IOException e) { - Stargate.debug("Stargate::migrateConfig", "Unable to save old backup and do migration"); - e.printStackTrace(); - return; - } - - //Read all available config migrations - Map migrationFields; - try { - migrationFields = FileHelper.readKeyValuePairs(FileHelper.getBufferedReaderFromInputStream( - FileHelper.getInputStreamForInternalFile("/config-migrations.txt")), "=", - ColorConversion.NORMAL); - } catch (IOException e) { - Stargate.debug("Stargate::migrateConfig", "Unable to load config migration file"); - e.printStackTrace(); - return; - } - - //Replace old config names with the new ones - for (String key : migrationFields.keySet()) { - if (newConfig.contains(key)) { - String newPath = migrationFields.get(key); - Object oldValue = newConfig.get(key); - if (!newPath.trim().isEmpty()) { - newConfig.set(newPath, oldValue); - } - newConfig.set(key, null); - } - } - } - /** * Loads economy from Vault */ private void setupVaultEconomy() { EconomyConfig economyConfig = getEconomyConfig(); - if (economyConfig.setupEconomy(Stargate.getPluginManager()) && economyConfig.getEconomy() != null) { + if (economyConfig.setupEconomy(Stargate.getPluginManager()) && economyConfig.getEconomy() != null && + economyConfig.getVault() != null) { String vaultVersion = economyConfig.getVault().getDescription().getVersion(); - Stargate.logInfo(Stargate.replaceVars(Stargate.getString("vaultLoaded"), "%version%", vaultVersion)); + Stargate.logInfo(new SGFormatBuilder(Message.VAULT_LOADED).replace("%version%", vaultVersion).toString()); } } @@ -497,18 +528,24 @@ public final class StargateConfig { * Creates missing folders */ private void createMissingFolders() { - File newPortalDir = new File(portalFolder); - if (!newPortalDir.exists()) { - if (!newPortalDir.mkdirs()) { - logger.severe("Unable to create portal directory"); - } - } + createMissingFolder(new File(gateFolder), "Unable to create gate directory"); + createMissingFolder(new File(portalFolder), "Unable to create portal directory"); File newFile = new File(portalFolder, Stargate.getInstance().getServer().getWorlds().get(0).getName() + ".db"); - if (!newFile.exists() && !newFile.getParentFile().exists()) { - if (!newFile.getParentFile().mkdirs()) { - logger.severe("Unable to create portal database folder: " + newFile.getParentFile().getPath()); - } + if (!newFile.exists() && !newFile.getParentFile().exists() && !newFile.getParentFile().mkdirs()) { + logger.severe("Unable to create portal database folder: " + newFile.getParentFile().getPath()); + } + } + + /** + * Creates the given folder if it's missing + * + * @param folder

The folder to create

+ * @param errorMessage

The error message to display if unable to create the folder

+ */ + private void createMissingFolder(File folder, String errorMessage) { + if (!folder.exists() && !folder.mkdirs()) { + logger.severe(errorMessage); } } @@ -517,6 +554,7 @@ public final class StargateConfig { * * @return

The portal folder

*/ + @NotNull public String getPortalFolder() { return portalFolder; } @@ -528,26 +566,57 @@ public final class StargateConfig { * * @return

The folder storing gate files

*/ + @NotNull public String getGateFolder() { return gateFolder; } - /** - * Gets the sender for sending messages to players - * - * @return

The sender for sending messages to players

- */ - public MessageSender getMessageSender() { - return messageSender; - } - /** * Gets the language loader containing translated strings * * @return

The language loader

*/ + @NotNull public LanguageLoader getLanguageLoader() { return languageLoader; } + /** + * Gets the string formatter to use + */ + @NotNull + private StringFormatter getStringFormatter() { + // In order to allow automatic customization of prefix color, parse it properly + String rawPrefix = getLanguageLoader().getString(Message.PREFIX); + String colorPattern = "(?:[&§][a-fA-F0-9klmnor]|&?#[0-9a-fA-F]{6}|§x(?:§[a-fA-F0-9]){6})*"; + Pattern pattern = Pattern.compile("(" + colorPattern + "\\[" + colorPattern + ")(\\w+)(" + + colorPattern + "]" + colorPattern + ")"); + + return getStringFormatter(rawPrefix, pattern); + } + + /** + * Gets the string formatter to use + * + * @param rawPrefix

The formatter prefix to parse

+ * @param pattern

The pattern to use for parsing

+ */ + private static @NotNull StringFormatter getStringFormatter(String rawPrefix, Pattern pattern) { + String prefix = rawPrefix; + String namePrefix = "["; + String nameSuffix = "]"; + Matcher matcher = pattern.matcher(rawPrefix); + if (matcher.find()) { + namePrefix = matcher.group(1).trim(); + prefix = matcher.group(2).trim(); + nameSuffix = matcher.group(3).trim(); + } + + StringFormatter stringFormatter = new StringFormatter(prefix, new Translator()); + stringFormatter.setColorConversion(ColorConversion.RGB); + stringFormatter.setNamePrefix(namePrefix); + stringFormatter.setNameSuffix(nameSuffix); + return stringFormatter; + } + } diff --git a/src/main/java/net/knarcraft/stargate/config/StargateGateConfig.java b/src/main/java/net/knarcraft/stargate/config/StargateGateConfig.java index f1dc7ca..9a4c2fd 100644 --- a/src/main/java/net/knarcraft/stargate/config/StargateGateConfig.java +++ b/src/main/java/net/knarcraft/stargate/config/StargateGateConfig.java @@ -6,6 +6,7 @@ import net.knarcraft.stargate.portal.PortalSignDrawer; import net.md_5.bungee.api.ChatColor; import org.bukkit.Color; import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.HashMap; @@ -26,7 +27,7 @@ public final class StargateGateConfig { * * @param configOptions

The loaded config options to use

*/ - public StargateGateConfig(Map configOptions) { + public StargateGateConfig(@NotNull Map configOptions) { this.configOptions = configOptions; loadGateConfig(); } @@ -100,6 +101,17 @@ public final class StargateGateConfig { return (boolean) configOptions.get(ConfigOption.HANDLE_CREATURE_TRANSPORTATION); } + /** + * Gets the delay to wait between each update of a Stargate's control block + * + *

This only affects the queued control updates during startup. It does not affect normal gameplay.

+ * + * @return

The amount of ticks to delay control updates by

+ */ + public int controlUpdateDelay() { + return (int) configOptions.get(ConfigOption.CONTROL_UPDATE_QUEUE_DELAY); + } + /** * Gets whether vehicles containing a creature, but not a player should be handled * @@ -248,8 +260,8 @@ public final class StargateGateConfig { * @param defaultColors

The specified default colors

* @param colorMaps

The list of color maps to save the resulting colors to

*/ - private void parsePerSignColors(Object signColorSpecification, ChatColor[] defaultColors, - List> colorMaps) { + private void parsePerSignColors(@NotNull Object signColorSpecification, @NotNull ChatColor[] defaultColors, + @NotNull List> colorMaps) { String[] specificationData = String.valueOf(signColorSpecification).split(":"); Material[] signMaterials = new Material[]{Material.matchMaterial(specificationData[0] + "_SIGN"), Material.matchMaterial(specificationData[0] + "_WALL_SIGN")}; @@ -280,8 +292,8 @@ public final class StargateGateConfig { * @param signMaterials

The materials to load this color for

* @param colorMaps

The list of color maps to save the resulting color to

*/ - private void loadPerSignColor(String[] colors, int colorIndex, ChatColor[] defaultColors, Material[] signMaterials, - List> colorMaps) { + private void loadPerSignColor(@NotNull String[] colors, int colorIndex, @NotNull ChatColor[] defaultColors, + @NotNull Material[] signMaterials, @NotNull List> colorMaps) { ChatColor parsedColor; if (colors[colorIndex].equalsIgnoreCase("inverted")) { //Convert from ChatColor to awt.Color to Bukkit.Color then invert and convert to ChatColor @@ -309,18 +321,20 @@ public final class StargateGateConfig { * * @param mainSignColor

A string representing the main sign color

*/ - private void loadPerSignColor(String mainSignColor, String highlightSignColor) { + private void loadPerSignColor(@NotNull String mainSignColor, @NotNull String highlightSignColor) { try { PortalSignDrawer.setMainColor(ChatColor.of(mainSignColor.toUpperCase())); } catch (IllegalArgumentException | NullPointerException exception) { - Stargate.logWarning("You have specified an invalid main sign color in your config.yml. Defaulting to BLACK"); + Stargate.logWarning("You have specified an invalid main sign color in your config.yml (" + mainSignColor + + "). Defaulting to BLACK"); PortalSignDrawer.setMainColor(ChatColor.BLACK); } try { PortalSignDrawer.setHighlightColor(ChatColor.of(highlightSignColor.toUpperCase())); } catch (IllegalArgumentException | NullPointerException exception) { - Stargate.logWarning("You have specified an invalid highlighting sign color in your config.yml. Defaulting to WHITE"); + Stargate.logWarning("You have specified an invalid highlighting sign color in your config.yml (" + + highlightSignColor + "). Defaulting to WHITE"); PortalSignDrawer.setHighlightColor(ChatColor.WHITE); } } diff --git a/src/main/java/net/knarcraft/stargate/config/material/BukkitMaterialSpecifier.java b/src/main/java/net/knarcraft/stargate/config/material/BukkitMaterialSpecifier.java new file mode 100644 index 0000000..b0b8ff5 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/config/material/BukkitMaterialSpecifier.java @@ -0,0 +1,49 @@ +package net.knarcraft.stargate.config.material; + +import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * A specifier for a Bukkit material + */ +public class BukkitMaterialSpecifier implements MaterialSpecifier { + + private final Material material; + + /** + * Instantiates a new material specifier + * + * @param material

The material to specify

+ */ + public BukkitMaterialSpecifier(@NotNull Material material) { + this.material = material; + } + + @Override + @NotNull + public String asString() { + return this.material.name(); + } + + @Override + @NotNull + public Set asMaterials() { + return Set.of(this.material); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof BukkitMaterialSpecifier bukkitMaterialSpecifier)) { + return false; + } + return this.material == bukkitMaterialSpecifier.material; + } + + @Override + public int hashCode() { + return material.hashCode(); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/config/material/BukkitTagSpecifier.java b/src/main/java/net/knarcraft/stargate/config/material/BukkitTagSpecifier.java new file mode 100644 index 0000000..5c38306 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/config/material/BukkitTagSpecifier.java @@ -0,0 +1,49 @@ +package net.knarcraft.stargate.config.material; + +import org.bukkit.Material; +import org.bukkit.Tag; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * A specifier for a Bukkit material tag + */ +public class BukkitTagSpecifier implements MaterialSpecifier { + + private final Tag tag; + + /** + * Instantiates a new tag specifier + * + * @param tag

The tag to specify

+ */ + public BukkitTagSpecifier(@NotNull Tag tag) { + this.tag = tag; + } + + @Override + public @NotNull String asString() { + return "#" + this.tag.getKey().toString().replaceFirst("minecraft:", ""); + } + + @Override + public @NotNull Set asMaterials() { + return this.tag.getValues(); + } + + + @Override + public boolean equals(Object other) { + if (!(other instanceof BukkitTagSpecifier bukkitMaterialSpecifier)) { + return false; + } + return this.tag == bukkitMaterialSpecifier.tag; + } + + @Override + public int hashCode() { + return tag.hashCode(); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/config/material/MaterialSpecifier.java b/src/main/java/net/knarcraft/stargate/config/material/MaterialSpecifier.java new file mode 100644 index 0000000..9665c05 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/config/material/MaterialSpecifier.java @@ -0,0 +1,29 @@ +package net.knarcraft.stargate.config.material; + +import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * An interface describing a specifier for one or more Bukkit materials + */ +public interface MaterialSpecifier { + + /** + * Gets the string representation of the material specifier + * + *

This is used when saving the value to a gate file

+ */ + @NotNull + String asString(); + + /** + * Gets all the materials the specifier specifies + * + *

This is used when registering gate materials

+ */ + @NotNull + Set asMaterials(); + +} diff --git a/src/main/java/net/knarcraft/stargate/container/BlockChangeRequest.java b/src/main/java/net/knarcraft/stargate/container/BlockChangeRequest.java index 55e5269..3603503 100644 --- a/src/main/java/net/knarcraft/stargate/container/BlockChangeRequest.java +++ b/src/main/java/net/knarcraft/stargate/container/BlockChangeRequest.java @@ -2,6 +2,8 @@ package net.knarcraft.stargate.container; import org.bukkit.Axis; import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Represents a request for changing a block into another material @@ -19,7 +21,7 @@ public class BlockChangeRequest { * @param material

The new material to change the block to

* @param axis

The new axis to orient the block along

*/ - public BlockChangeRequest(BlockLocation blockLocation, Material material, Axis axis) { + public BlockChangeRequest(@NotNull BlockLocation blockLocation, @NotNull Material material, @Nullable Axis axis) { this.blockLocation = blockLocation; newMaterial = material; newAxis = axis; @@ -30,6 +32,7 @@ public class BlockChangeRequest { * * @return

The location of the block

*/ + @NotNull public BlockLocation getBlockLocation() { return blockLocation; } @@ -39,6 +42,7 @@ public class BlockChangeRequest { * * @return

The material to change the block into

*/ + @NotNull public Material getMaterial() { return newMaterial; } @@ -48,6 +52,7 @@ public class BlockChangeRequest { * * @return

The axis to orient the block along

*/ + @Nullable public Axis getAxis() { return newAxis; } diff --git a/src/main/java/net/knarcraft/stargate/container/BlockLocation.java b/src/main/java/net/knarcraft/stargate/container/BlockLocation.java index bb8b533..bf84940 100644 --- a/src/main/java/net/knarcraft/stargate/container/BlockLocation.java +++ b/src/main/java/net/knarcraft/stargate/container/BlockLocation.java @@ -10,6 +10,8 @@ import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; import org.bukkit.block.data.type.Sign; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * This class represents a block location @@ -30,7 +32,7 @@ public class BlockLocation extends Location { * @param y

The y coordinate of the block

* @param z

The z coordinate of the block

*/ - public BlockLocation(World world, int x, int y, int z) { + public BlockLocation(@NotNull World world, int x, int y, int z) { super(world, x, y, z); } @@ -39,7 +41,7 @@ public class BlockLocation extends Location { * * @param block

The block to get the location of

*/ - public BlockLocation(Block block) { + public BlockLocation(@NotNull Block block) { super(block.getWorld(), block.getX(), block.getY(), block.getZ()); } @@ -49,7 +51,7 @@ public class BlockLocation extends Location { * @param world

The world the block exists in

* @param string

A comma separated list of x, y and z coordinates as integers

*/ - public BlockLocation(World world, String string) { + public BlockLocation(@NotNull World world, @NotNull String string) { super(world, Integer.parseInt(string.split(",")[0]), Integer.parseInt(string.split(",")[1]), Integer.parseInt(string.split(",")[2])); } @@ -62,6 +64,7 @@ public class BlockLocation extends Location { * @param z

The number of blocks to move in the z-direction

* @return

A new block location

*/ + @NotNull public BlockLocation makeRelativeBlockLocation(int x, int y, int z) { return (BlockLocation) this.clone().add(x, y, z); } @@ -75,6 +78,7 @@ public class BlockLocation extends Location { * @param yaw

The number of blocks to move in the z-direction

* @return

A new location

*/ + @NotNull public Location makeRelativeLocation(double x, double y, double z, float yaw) { Location newLocation = this.clone(); newLocation.setYaw(yaw); @@ -89,9 +93,10 @@ public class BlockLocation extends Location { * @param yaw

The yaw pointing outwards from a portal (in the relative vector's out direction)

* @return

A location relative to this location

*/ - public BlockLocation getRelativeLocation(RelativeBlockVector relativeVector, double yaw) { - Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(relativeVector.getRight(), - relativeVector.getDown(), relativeVector.getOut(), yaw); + @NotNull + public BlockLocation getRelativeLocation(@NotNull RelativeBlockVector relativeVector, double yaw) { + Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(relativeVector.right(), + relativeVector.down(), relativeVector.out(), yaw); return makeRelativeBlockLocation(realVector.getBlockX(), realVector.getBlockY(), realVector.getBlockZ()); } @@ -107,6 +112,7 @@ public class BlockLocation extends Location { * @param portalYaw

The yaw when looking out from the portal

* @return A new location relative to this block location */ + @NotNull public Location getRelativeLocation(double right, double down, double out, float portalYaw) { Vector realVector = DirectionHelper.getCoordinateVectorFromRelativeVector(right, down, out, portalYaw); return makeRelativeLocation(0.5 + realVector.getBlockX(), realVector.getBlockY(), @@ -118,6 +124,7 @@ public class BlockLocation extends Location { * * @return

The block's material type

*/ + @NotNull public Material getType() { return this.getBlock().getType(); } @@ -127,7 +134,7 @@ public class BlockLocation extends Location { * * @param type

The block's new material type

*/ - public void setType(Material type) { + public void setType(@NotNull Material type) { this.getBlock().setType(type); } @@ -136,6 +143,7 @@ public class BlockLocation extends Location { * * @return

The location representing this block location

*/ + @NotNull public Location getLocation() { return this.clone(); } @@ -148,6 +156,7 @@ public class BlockLocation extends Location { * * @return

This block location's parent block

*/ + @Nullable public Block getParent() { if (parent == null) { findParent(); @@ -184,6 +193,7 @@ public class BlockLocation extends Location { } @Override + @NotNull public String toString() { return String.valueOf(this.getBlockX()) + ',' + this.getBlockY() + ',' + this.getBlockZ(); } @@ -203,7 +213,7 @@ public class BlockLocation extends Location { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (this == object) { return true; } diff --git a/src/main/java/net/knarcraft/stargate/container/ChunkUnloadRequest.java b/src/main/java/net/knarcraft/stargate/container/ChunkUnloadRequest.java index 4c47d15..de62651 100644 --- a/src/main/java/net/knarcraft/stargate/container/ChunkUnloadRequest.java +++ b/src/main/java/net/knarcraft/stargate/container/ChunkUnloadRequest.java @@ -17,7 +17,7 @@ public class ChunkUnloadRequest implements Comparable { * @param chunkToUnload

The chunk to request the unloading of

* @param timeUntilUnload

The time in milliseconds to wait before unloading the chunk

*/ - public ChunkUnloadRequest(Chunk chunkToUnload, Long timeUntilUnload) { + public ChunkUnloadRequest(@NotNull Chunk chunkToUnload, @NotNull Long timeUntilUnload) { this.chunkToUnload = chunkToUnload; long systemNanoTime = System.nanoTime(); this.unloadNanoTime = systemNanoTime + (timeUntilUnload * 1000000); @@ -28,6 +28,7 @@ public class ChunkUnloadRequest implements Comparable { * * @return

The chunk to unload

*/ + @NotNull public Chunk getChunkToUnload() { return this.chunkToUnload; } @@ -37,11 +38,13 @@ public class ChunkUnloadRequest implements Comparable { * * @return

The system nano time denoting when the chunk is to be unloaded

*/ + @NotNull public Long getUnloadNanoTime() { return this.unloadNanoTime; } @Override + @NotNull public String toString() { return "{" + chunkToUnload + ", " + unloadNanoTime + "}"; } diff --git a/src/main/java/net/knarcraft/stargate/container/ControlBlockUpdateRequest.java b/src/main/java/net/knarcraft/stargate/container/ControlBlockUpdateRequest.java new file mode 100644 index 0000000..a68c0c9 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/container/ControlBlockUpdateRequest.java @@ -0,0 +1,12 @@ +package net.knarcraft.stargate.container; + +import net.knarcraft.stargate.portal.Portal; +import org.jetbrains.annotations.NotNull; + +/** + * A request for updating a portal's control blocks + * + * @param portal

The portal to update the control blocks for

+ */ +public record ControlBlockUpdateRequest(@NotNull Portal portal) { +} diff --git a/src/main/java/net/knarcraft/stargate/container/FromTheEndTeleportation.java b/src/main/java/net/knarcraft/stargate/container/FromTheEndTeleportation.java index 54c7171..736f847 100644 --- a/src/main/java/net/knarcraft/stargate/container/FromTheEndTeleportation.java +++ b/src/main/java/net/knarcraft/stargate/container/FromTheEndTeleportation.java @@ -1,60 +1,16 @@ package net.knarcraft.stargate.container; import net.knarcraft.stargate.portal.Portal; -import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; /** - * This class represents a player teleporting from the end to the over-world using an artificial end portal + * This class represents a teleportation from the end to the over-world using an artificial end portal * *

This is necessary because a player entering an end portal in the end is a special case. Instead of being * teleported, the player is respawned. Because of this, the teleportation needs to be saved and later used to hijack * the position of where the player is to respawn.

+ * + * @param exitPortal

The portal the player should exit from when arriving in the over-world

*/ -public class FromTheEndTeleportation { - - private final Player teleportingPlayer; - private final Portal exitPortal; - - /** - * Instantiates a new teleportation from the end - * - * @param teleportingPlayer

The teleporting player

- * @param exitPortal

The portal to exit from

- */ - public FromTheEndTeleportation(Player teleportingPlayer, Portal exitPortal) { - this.teleportingPlayer = teleportingPlayer; - this.exitPortal = exitPortal; - } - - /** - * Gets the teleporting player - * - * @return

The teleporting player

- */ - public Player getPlayer() { - return this.teleportingPlayer; - } - - /** - * Gets the portal to exit from - * - * @return

The portal to exit from

- */ - public Portal getExit() { - return this.exitPortal; - } - - @Override - public int hashCode() { - return teleportingPlayer.hashCode(); - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof FromTheEndTeleportation otherTeleportation)) { - return false; - } - return teleportingPlayer.equals(otherTeleportation.teleportingPlayer); - } - +public record FromTheEndTeleportation(@NotNull Portal exitPortal) { } diff --git a/src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java b/src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java index 8cb6942..b343640 100644 --- a/src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java +++ b/src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java @@ -2,6 +2,9 @@ package net.knarcraft.stargate.container; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + /** * This stores a block location as a vector relative to a position * @@ -9,62 +12,48 @@ import org.bukkit.util.Vector; * top-left block of a gate (top-left when looking at the side with the sign). The right is therefore the distance * from the top-left corner towards the top-right corner. Down is the distance from the top-left corner towards the * bottom-left corner. Out is the distance outward from the gate.

+ * + *

Relative block vectors start from a top-left corner. A yaw is used to orient a relative block vector in the + * "real world". In terms of a gate layout, the origin is 0,0. Right is towards the end of the line. Down is to the + * next line. Out is towards the observer.

+ * + * @param right

The distance rightward relative to the origin

+ * @param down

The distance downward relative to the origin

+ * @param out

The distance outward relative to the origin

*/ -public class RelativeBlockVector { - - private final int right; - private final int down; - private final int out; +public record RelativeBlockVector(int right, int down, int out) { /** - * A specifier for one of the relative block vector's three properties + * Adds the given value to this relative block vector's "right" property + * + * @param valueToAdd

The value to add

+ * @return

The new resulting vector

*/ - public enum Property { - /** - * Specifies the relative block vector's right property - */ - RIGHT, - /** - * Specifies the relative block vector's down property - */ - DOWN, - /** - * Specifies the relative block vector's out property - */ - OUT + @NotNull + public RelativeBlockVector addRight(int valueToAdd) { + return new RelativeBlockVector(this.right + valueToAdd, this.down, this.out); } /** - * Instantiates a new relative block vector + * Adds the given value to this relative block vector's "down" property * - *

Relative block vectors start from a top-left corner. A yaw is used to orient a relative block vector in the - * "real world". - * In terms of a gate layout, the origin is 0,0. Right is towards the end of the line. Down is to the - * next line. Out is towards the observer.

- * - * @param right

The distance rightward relative to the origin

- * @param down

The distance downward relative to the origin

- * @param out

The distance outward relative to the origin

+ * @param valueToAdd

The value to add

+ * @return

The new resulting vector

*/ - public RelativeBlockVector(int right, int down, int out) { - this.right = right; - this.down = down; - this.out = out; + @NotNull + public RelativeBlockVector addDown(int valueToAdd) { + return new RelativeBlockVector(this.right, this.down + valueToAdd, this.out); } /** - * Adds a value to one of the properties of this relative block vector + * Adds the given value to this relative block vector's "out" property * - * @param propertyToAddTo

The property to add to

- * @param valueToAdd

The value to add to the property (negative to move in the opposite direction)

- * @return

A new relative block vector with the property altered

+ * @param valueToAdd

The value to add

+ * @return

The new resulting vector

*/ - public RelativeBlockVector addToVector(Property propertyToAddTo, int valueToAdd) { - return switch (propertyToAddTo) { - case RIGHT -> new RelativeBlockVector(this.right + valueToAdd, this.down, this.out); - case DOWN -> new RelativeBlockVector(this.right, this.down + valueToAdd, this.out); - case OUT -> new RelativeBlockVector(this.right, this.down, this.out + valueToAdd); - }; + @NotNull + public RelativeBlockVector addOut(int valueToAdd) { + return new RelativeBlockVector(this.right, this.down, this.out + valueToAdd); } /** @@ -81,44 +70,19 @@ public class RelativeBlockVector { * * @return

This vector, but inverted

*/ + @NotNull public RelativeBlockVector invert() { return new RelativeBlockVector(-this.right, -this.down, -this.out); } - /** - * Gets the distance to the right relative to the origin - * - * @return

The distance to the right relative to the origin

- */ - public int getRight() { - return right; - } - - /** - * Gets the distance downward relative to the origin - * - * @return

The distance downward relative to the origin

- */ - public int getDown() { - return down; - } - - /** - * Gets the distance outward relative to the origin - * - * @return

The distance outward relative to the origin

- */ - public int getOut() { - return out; - } - @Override + @NotNull public String toString() { return String.format("(right = %d, down = %d, out = %d)", right, down, out); } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other == this) { return true; } diff --git a/src/main/java/net/knarcraft/stargate/container/SignData.java b/src/main/java/net/knarcraft/stargate/container/SignData.java index 1b4bd31..8d65caa 100644 --- a/src/main/java/net/knarcraft/stargate/container/SignData.java +++ b/src/main/java/net/knarcraft/stargate/container/SignData.java @@ -1,9 +1,11 @@ package net.knarcraft.stargate.container; import net.knarcraft.knarlib.util.ColorHelper; +import net.knarcraft.stargate.utility.SignHelper; import net.md_5.bungee.api.ChatColor; import org.bukkit.DyeColor; import org.bukkit.block.Sign; +import org.jetbrains.annotations.NotNull; /** * A class that keeps track of the sign colors for a given sign @@ -22,11 +24,11 @@ public class SignData { * @param mainSignColor

The main color to use for the sign

* @param highlightSignColor

The highlighting color to use for the sign

*/ - public SignData(Sign sign, ChatColor mainSignColor, ChatColor highlightSignColor) { + public SignData(@NotNull Sign sign, @NotNull ChatColor mainSignColor, @NotNull ChatColor highlightSignColor) { this.sign = sign; this.mainSignColor = mainSignColor; this.highlightSignColor = highlightSignColor; - this.dyedColor = sign.getColor(); + this.dyedColor = SignHelper.getDye(sign); } /** @@ -34,6 +36,7 @@ public class SignData { * * @return

The sign of this sign colors object

*/ + @NotNull public Sign getSign() { return sign; } @@ -43,6 +46,7 @@ public class SignData { * * @return

The main color of the sign

*/ + @NotNull public ChatColor getMainSignColor() { if (dyedColor != DyeColor.BLACK) { return ColorHelper.fromColor(dyedColor.getColor()); @@ -56,6 +60,7 @@ public class SignData { * * @return

The highlighting color of the sign

*/ + @NotNull public ChatColor getHighlightSignColor() { if (dyedColor != DyeColor.BLACK) { return ColorHelper.fromColor(ColorHelper.invert(dyedColor.getColor())); diff --git a/src/main/java/net/knarcraft/stargate/event/StargateAccessEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateAccessEvent.java index 5b64a14..83328ac 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateAccessEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateAccessEvent.java @@ -24,7 +24,7 @@ public class StargateAccessEvent extends StargatePlayerEvent { * @param portal

The portal involved in the event

* @param deny

Whether the stargate access should be denied

*/ - public StargateAccessEvent(Player player, Portal portal, boolean deny) { + public StargateAccessEvent(@NotNull Player player, @NotNull Portal portal, boolean deny) { super(portal, player); this.deny = deny; @@ -53,6 +53,7 @@ public class StargateAccessEvent extends StargatePlayerEvent { * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateActivateEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateActivateEvent.java index 3d09661..6509d90 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateActivateEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateActivateEvent.java @@ -28,7 +28,8 @@ public class StargateActivateEvent extends StargatePlayerEvent { * @param destinations

The destinations available to the player using the portal

* @param destination

The currently selected destination

*/ - public StargateActivateEvent(Portal portal, Player player, List destinations, String destination) { + public StargateActivateEvent(@NotNull Portal portal, @NotNull Player player, @NotNull List destinations, + @NotNull String destination) { super(portal, player); this.destinations = destinations; @@ -40,6 +41,7 @@ public class StargateActivateEvent extends StargatePlayerEvent { * * @return

The destinations available for the portal

*/ + @NotNull public List getDestinations() { return destinations; } @@ -49,7 +51,7 @@ public class StargateActivateEvent extends StargatePlayerEvent { * * @param destinations

The new list of available destinations

*/ - public void setDestinations(List destinations) { + public void setDestinations(@NotNull List destinations) { this.destinations = destinations; } @@ -58,6 +60,7 @@ public class StargateActivateEvent extends StargatePlayerEvent { * * @return

The selected destination

*/ + @NotNull public String getDestination() { return destination; } @@ -67,7 +70,7 @@ public class StargateActivateEvent extends StargatePlayerEvent { * * @param destination

The new selected destination

*/ - public void setDestination(String destination) { + public void setDestination(@NotNull String destination) { this.destination = destination; } @@ -76,6 +79,7 @@ public class StargateActivateEvent extends StargatePlayerEvent { * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateCloseEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateCloseEvent.java index 3f2ff07..2a00e52 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateCloseEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateCloseEvent.java @@ -22,7 +22,7 @@ public class StargateCloseEvent extends StargateEvent { * @param portal

The portal to close

* @param force

Whether to force the gate to close, even if set as always-on

*/ - public StargateCloseEvent(Portal portal, boolean force) { + public StargateCloseEvent(@NotNull Portal portal, boolean force) { super(portal); this.force = force; @@ -51,6 +51,7 @@ public class StargateCloseEvent extends StargateEvent { * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateCreateEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateCreateEvent.java index 1f66e60..20b58c1 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateCreateEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateCreateEvent.java @@ -29,7 +29,8 @@ public class StargateCreateEvent extends StargatePlayerEvent { * @param denyReason

The reason stargate creation was denied

* @param cost

The cost of creating the new star gate

*/ - public StargateCreateEvent(Player player, Portal portal, String[] lines, boolean deny, String denyReason, int cost) { + public StargateCreateEvent(@NotNull Player player, @NotNull Portal portal, @NotNull String[] lines, boolean deny, + @NotNull String denyReason, int cost) { super(portal, player); this.lines = lines; this.deny = deny; @@ -44,6 +45,7 @@ public class StargateCreateEvent extends StargatePlayerEvent { * @return

The text on the given line

* @throws IndexOutOfBoundsException

If given a line index less than zero or above three

*/ + @NotNull public String getLine(int index) throws IndexOutOfBoundsException { return lines[index]; } @@ -71,6 +73,7 @@ public class StargateCreateEvent extends StargatePlayerEvent { * * @return

The reason the stargate creation was denied

*/ + @NotNull public String getDenyReason() { return denyReason; } @@ -80,7 +83,7 @@ public class StargateCreateEvent extends StargatePlayerEvent { * * @param denyReason

The new reason why the stargate creation was denied

*/ - public void setDenyReason(String denyReason) { + public void setDenyReason(@NotNull String denyReason) { this.denyReason = denyReason; } @@ -107,6 +110,7 @@ public class StargateCreateEvent extends StargatePlayerEvent { * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateDeactivateEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateDeactivateEvent.java index 3314ac1..c81e901 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateDeactivateEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateDeactivateEvent.java @@ -20,7 +20,7 @@ public class StargateDeactivateEvent extends StargateEvent { * * @param portal

The portal which was deactivated

*/ - public StargateDeactivateEvent(Portal portal) { + public StargateDeactivateEvent(@NotNull Portal portal) { super(portal); } @@ -29,6 +29,7 @@ public class StargateDeactivateEvent extends StargateEvent { * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateDestroyEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateDestroyEvent.java index bfcc606..3e9a6f5 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateDestroyEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateDestroyEvent.java @@ -27,7 +27,8 @@ public class StargateDestroyEvent extends StargatePlayerEvent { * @param denyMsg

The message to display if the event is denied

* @param cost

The cost of destroying the portal

*/ - public StargateDestroyEvent(Portal portal, Player player, boolean deny, String denyMsg, int cost) { + public StargateDestroyEvent(@NotNull Portal portal, @NotNull Player player, boolean deny, @NotNull String denyMsg, + int cost) { super(portal, player); this.deny = deny; this.denyReason = denyMsg; @@ -57,6 +58,7 @@ public class StargateDestroyEvent extends StargatePlayerEvent { * * @return

The reason the event was denied

*/ + @NotNull public String getDenyReason() { return denyReason; } @@ -66,7 +68,7 @@ public class StargateDestroyEvent extends StargatePlayerEvent { * * @param denyReason

The reason the event was denied

*/ - public void setDenyReason(String denyReason) { + public void setDenyReason(@NotNull String denyReason) { this.denyReason = denyReason; } @@ -93,6 +95,7 @@ public class StargateDestroyEvent extends StargatePlayerEvent { * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateEntityPortalEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateEntityPortalEvent.java index cd34504..5f4a4a1 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateEntityPortalEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateEntityPortalEvent.java @@ -27,7 +27,8 @@ public class StargateEntityPortalEvent extends StargateEvent implements Stargate * @param destination

The destination the entity should exit from

* @param exit

The exit location of the destination portal the entity will be teleported to

*/ - public StargateEntityPortalEvent(Entity travellingEntity, Portal portal, Portal destination, Location exit) { + public StargateEntityPortalEvent(@NotNull Entity travellingEntity, @NotNull Portal portal, + @NotNull Portal destination, @NotNull Location exit) { super(portal); this.travellingEntity = travellingEntity; @@ -40,6 +41,7 @@ public class StargateEntityPortalEvent extends StargateEvent implements Stargate * * @return

The non-player teleporting

*/ + @NotNull public Entity getEntity() { return travellingEntity; } @@ -49,6 +51,7 @@ public class StargateEntityPortalEvent extends StargateEvent implements Stargate * * @return

The destination portal

*/ + @NotNull public Portal getDestination() { return destination; } @@ -59,6 +62,7 @@ public class StargateEntityPortalEvent extends StargateEvent implements Stargate * @return

Location of the exit point

*/ @Override + @NotNull public Location getExit() { return exit; } @@ -68,7 +72,7 @@ public class StargateEntityPortalEvent extends StargateEvent implements Stargate * * @param location

The new location of the entity's exit point

*/ - public void setExit(Location location) { + public void setExit(@NotNull Location location) { this.exit = location; } @@ -77,6 +81,7 @@ public class StargateEntityPortalEvent extends StargateEvent implements Stargate * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateEvent.java index f67a388..911937f 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateEvent.java @@ -3,6 +3,7 @@ package net.knarcraft.stargate.event; import net.knarcraft.stargate.portal.Portal; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; +import org.jetbrains.annotations.NotNull; /** * An abstract event describing any stargate event @@ -18,7 +19,7 @@ public abstract class StargateEvent extends Event implements Cancellable { * * @param portal

The portal involved in this stargate event

*/ - StargateEvent(Portal portal) { + StargateEvent(@NotNull Portal portal) { this.portal = portal; this.cancelled = false; } @@ -28,6 +29,7 @@ public abstract class StargateEvent extends Event implements Cancellable { * * @return

The portal involved in this stargate event

*/ + @NotNull public Portal getPortal() { return portal; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateOpenEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateOpenEvent.java index ce10534..0767f05 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateOpenEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateOpenEvent.java @@ -4,6 +4,7 @@ import net.knarcraft.stargate.portal.Portal; import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * This event should be called whenever a player opens a stargate @@ -23,7 +24,7 @@ public class StargateOpenEvent extends StargatePlayerEvent { * @param portal

The opened portal

* @param force

Whether to force the portal open

*/ - public StargateOpenEvent(Player player, Portal portal, boolean force) { + public StargateOpenEvent(@Nullable Player player, @NotNull Portal portal, boolean force) { super(portal, player); this.force = force; @@ -52,6 +53,7 @@ public class StargateOpenEvent extends StargatePlayerEvent { * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargatePlayerEvent.java b/src/main/java/net/knarcraft/stargate/event/StargatePlayerEvent.java index 14514f6..ff701e4 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargatePlayerEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargatePlayerEvent.java @@ -2,6 +2,8 @@ package net.knarcraft.stargate.event; import net.knarcraft.stargate.portal.Portal; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * An abstract event describing any stargate event where a player is involved @@ -16,7 +18,7 @@ public abstract class StargatePlayerEvent extends StargateEvent { * * @param portal

The portal involved in this stargate event

*/ - StargatePlayerEvent(Portal portal, Player player) { + StargatePlayerEvent(@NotNull Portal portal, @Nullable Player player) { super(portal); this.player = player; } @@ -26,6 +28,7 @@ public abstract class StargatePlayerEvent extends StargateEvent { * * @return

The player creating the star gate

*/ + @Nullable public Player getPlayer() { return player; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargatePlayerPortalEvent.java b/src/main/java/net/knarcraft/stargate/event/StargatePlayerPortalEvent.java index 3d2526a..6bb4f88 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargatePlayerPortalEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargatePlayerPortalEvent.java @@ -26,7 +26,8 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent implements St * @param destination

The destination the player should exit from

* @param exit

The exit location of the destination portal the user will be teleported to

*/ - public StargatePlayerPortalEvent(Player player, Portal portal, Portal destination, Location exit) { + public StargatePlayerPortalEvent(@NotNull Player player, @NotNull Portal portal, @NotNull Portal destination, + @NotNull Location exit) { super(portal, player); this.destination = destination; @@ -38,6 +39,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent implements St * * @return

The destination portal

*/ + @NotNull public Portal getDestination() { return destination; } @@ -48,6 +50,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent implements St * @return

Location of the exit point

*/ @Override + @NotNull public Location getExit() { return exit; } @@ -57,7 +60,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent implements St * * @param location

The new location of the player's exit point

*/ - public void setExit(Location location) { + public void setExit(@NotNull Location location) { this.exit = location; } @@ -66,6 +69,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent implements St * * @return

A handler-list with all event handlers

*/ + @NotNull public static HandlerList getHandlerList() { return handlers; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateTeleportEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateTeleportEvent.java index e440828..35bef4e 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateTeleportEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateTeleportEvent.java @@ -2,6 +2,7 @@ package net.knarcraft.stargate.event; import org.bukkit.Location; import org.bukkit.event.Cancellable; +import org.jetbrains.annotations.NotNull; /** * A generic teleportation event @@ -13,6 +14,7 @@ public interface StargateTeleportEvent extends Cancellable { * * @return

Location of the exit point

*/ + @NotNull Location getExit(); } diff --git a/src/main/java/net/knarcraft/stargate/listener/BlockEventListener.java b/src/main/java/net/knarcraft/stargate/listener/BlockEventListener.java index f443569..0200d93 100644 --- a/src/main/java/net/knarcraft/stargate/listener/BlockEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/BlockEventListener.java @@ -1,7 +1,10 @@ package net.knarcraft.stargate.listener; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.container.BlockChangeRequest; +import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.event.StargateDestroyEvent; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalCreator; @@ -28,13 +31,13 @@ import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.EntityBlockFormEvent; import org.bukkit.event.block.SignChangeEvent; +import org.jetbrains.annotations.NotNull; import java.util.List; /** * This class is responsible for listening to relevant block events related to creating and breaking portals */ -@SuppressWarnings("unused") public class BlockEventListener implements Listener { /** @@ -45,9 +48,9 @@ public class BlockEventListener implements Listener { * * @param event

The triggered event

*/ - @EventHandler - public void onBlockFormedByEntity(EntityBlockFormEvent event) { - if (event.isCancelled() || (!Stargate.getGateConfig().protectEntrance() && + @EventHandler(ignoreCancelled = true) + public void onBlockFormedByEntity(@NotNull EntityBlockFormEvent event) { + if ((!Stargate.getGateConfig().protectEntrance() && !Stargate.getGateConfig().verifyPortals())) { return; } @@ -66,11 +69,8 @@ public class BlockEventListener implements Listener { * * @param event

The triggered event

*/ - @EventHandler - public void onSignChange(SignChangeEvent event) { - if (event.isCancelled()) { - return; - } + @EventHandler(ignoreCancelled = true) + public void onSignChange(@NotNull SignChangeEvent event) { Player player = event.getPlayer(); Block block = event.getBlock(); //Ignore normal signs @@ -88,23 +88,21 @@ public class BlockEventListener implements Listener { if (portal.getOptions().hasNoSign()) { Material replaceMaterial = PortalFileHelper.decideRemovalMaterial(portal.getSignLocation(), portal); BlockChangeRequest request = new BlockChangeRequest(portal.getSignLocation(), replaceMaterial, null); - Stargate.addBlockChangeRequest(request); + Stargate.addControlBlockUpdateRequest(request); } - Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("createMsg")); + new SGFormatBuilder(Message.CREATED).success(player); Stargate.debug("onSignChange", "Initialized stargate: " + portal.getName()); Stargate.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), portal::drawSign, 1); } - @EventHandler(priority = EventPriority.HIGHEST) - public void onBlockPlace(BlockPlaceEvent event) { - if (event.isCancelled() || !Stargate.getGateConfig().protectEntrance()) { + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onBlockPlace(@NotNull BlockPlaceEvent event) { + if (!Stargate.getGateConfig().protectEntrance()) { return; } Block block = event.getBlock(); - Player player = event.getPlayer(); - Portal portal = PortalHandler.getByEntrance(block); if (portal != null) { //Prevent blocks from being placed in the entrance, if protectEntrance is enabled, as breaking the block @@ -118,11 +116,8 @@ public class BlockEventListener implements Listener { * * @param event

The triggered event

*/ - @EventHandler(priority = EventPriority.HIGHEST) - public void onBlockBreak(BlockBreakEvent event) { - if (event.isCancelled()) { - return; - } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onBlockBreak(@NotNull BlockBreakEvent event) { Block block = event.getBlock(); Player player = event.getPlayer(); @@ -138,9 +133,16 @@ public class BlockEventListener implements Listener { boolean deny = false; String denyMessage = ""; + // Block breaking the button from breaking the entire Stargate + if (portal.getStructure().getButton() != null && portal.getStructure().getButton().equals( + new BlockLocation(event.getBlock()))) { + event.setCancelled(true); + return; + } + //Decide if the user can destroy the portal if (!PermissionHelper.canDestroyPortal(player, portal)) { - denyMessage = Stargate.getString("denyMsg"); + denyMessage = new SGFormatBuilder(Message.ACCESS_DENIED).toString(); deny = true; Stargate.logInfo(String.format("%s tried to destroy gate", player.getName())); } @@ -158,7 +160,7 @@ public class BlockEventListener implements Listener { //Destroy denied if (destroyEvent.getDeny()) { if (!destroyEvent.getDenyReason().trim().isEmpty()) { - Stargate.getMessageSender().sendErrorMessage(player, destroyEvent.getDenyReason()); + new SGFormatBuilder(destroyEvent.getDenyReason()).error(player); } event.setCancelled(true); return; @@ -170,7 +172,7 @@ public class BlockEventListener implements Listener { } PortalRegistry.unregisterPortal(portal, true); - Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("destroyMsg")); + new SGFormatBuilder(Message.DESTROYED).success(player); } /** @@ -182,8 +184,8 @@ public class BlockEventListener implements Listener { * @param event

The break event

* @return

True if the payment was successful. False if the event was cancelled

*/ - private boolean handleEconomyPayment(StargateDestroyEvent destroyEvent, Player player, Portal portal, - BlockBreakEvent event) { + private boolean handleEconomyPayment(@NotNull StargateDestroyEvent destroyEvent, @NotNull Player player, + @NotNull Portal portal, @NotNull BlockBreakEvent event) { int cost = destroyEvent.getCost(); if (cost != 0) { String portalName = portal.getName(); @@ -209,8 +211,8 @@ public class BlockEventListener implements Listener { * * @param event

The event to check and possibly cancel

*/ - @EventHandler - public void onBlockPhysics(BlockPhysicsEvent event) { + @EventHandler(ignoreCancelled = true) + public void onBlockPhysics(@NotNull BlockPhysicsEvent event) { Block block = event.getBlock(); Portal portal = null; @@ -229,12 +231,12 @@ public class BlockEventListener implements Listener { * * @param event

The event to check and possibly cancel

*/ - @EventHandler - public void onBlockFromTo(BlockFromToEvent event) { + @EventHandler(ignoreCancelled = true) + public void onBlockFromTo(@NotNull BlockFromToEvent event) { Portal portal = PortalHandler.getByEntrance(event.getBlock()); - if (portal != null) { - event.setCancelled((event.getBlock().getY() == event.getToBlock().getY())); + if (portal != null && event.getBlock().getY() == event.getToBlock().getY()) { + event.setCancelled(true); } } @@ -243,8 +245,8 @@ public class BlockEventListener implements Listener { * * @param event

The event to check and possibly cancel

*/ - @EventHandler - public void onPistonExtend(BlockPistonExtendEvent event) { + @EventHandler(ignoreCancelled = true) + public void onPistonExtend(@NotNull BlockPistonExtendEvent event) { cancelPistonEvent(event, event.getBlocks()); } @@ -253,8 +255,8 @@ public class BlockEventListener implements Listener { * * @param event

The event to check and possibly cancel

*/ - @EventHandler - public void onPistonRetract(BlockPistonRetractEvent event) { + @EventHandler(ignoreCancelled = true) + public void onPistonRetract(@NotNull BlockPistonRetractEvent event) { if (!event.isSticky()) { return; } @@ -267,7 +269,7 @@ public class BlockEventListener implements Listener { * @param event

The event to cancel

* @param blocks

The blocks included in the event

*/ - private void cancelPistonEvent(BlockPistonEvent event, List blocks) { + private void cancelPistonEvent(@NotNull BlockPistonEvent event, @NotNull List blocks) { for (Block block : blocks) { Portal portal = PortalHandler.getByBlock(block); if (portal != null) { diff --git a/src/main/java/net/knarcraft/stargate/listener/EntityEventListener.java b/src/main/java/net/knarcraft/stargate/listener/EntityEventListener.java index cc181c2..adb6d06 100644 --- a/src/main/java/net/knarcraft/stargate/listener/EntityEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/EntityEventListener.java @@ -12,6 +12,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityPortalEvent; +import org.jetbrains.annotations.NotNull; /** * This listener listens for any relevant events on portal entities @@ -25,7 +26,7 @@ public class EntityEventListener implements Listener { * @param event

The event to check and possibly cancel

*/ @EventHandler(priority = EventPriority.LOWEST) - public void onPortalEvent(EntityPortalEvent event) { + public void onPortalEvent(@NotNull EntityPortalEvent event) { if (event.isCancelled()) { return; } @@ -46,7 +47,7 @@ public class EntityEventListener implements Listener { * @param event

The triggered explosion event

*/ @EventHandler - public void onEntityExplode(EntityExplodeEvent event) { + public void onEntityExplode(@NotNull EntityExplodeEvent event) { if (event.isCancelled()) { return; } diff --git a/src/main/java/net/knarcraft/stargate/listener/EntitySpawnListener.java b/src/main/java/net/knarcraft/stargate/listener/EntitySpawnListener.java index b4c0b2c..f806e6c 100644 --- a/src/main/java/net/knarcraft/stargate/listener/EntitySpawnListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/EntitySpawnListener.java @@ -5,6 +5,7 @@ import net.knarcraft.stargate.portal.PortalHandler; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; +import org.jetbrains.annotations.NotNull; /** * A listener that listens for any relevant events causing entities to spawn @@ -12,13 +13,12 @@ import org.bukkit.event.entity.CreatureSpawnEvent; public class EntitySpawnListener implements Listener { @EventHandler - public void onCreatureSpawn(CreatureSpawnEvent event) { + public void onCreatureSpawn(@NotNull CreatureSpawnEvent event) { //Prevent Zombified Piglins and other creatures form spawning at stargates - if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.NETHER_PORTAL) { - if (PortalHandler.getByEntrance(event.getLocation()) != null) { - event.setCancelled(true); - Stargate.debug("EntitySpawnListener", "Prevented creature from spawning at Stargate"); - } + if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.NETHER_PORTAL && + PortalHandler.getByEntrance(event.getLocation()) != null) { + event.setCancelled(true); + Stargate.debug("EntitySpawnListener", "Prevented creature from spawning at Stargate"); } } diff --git a/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java b/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java index 6fd69b0..cac793e 100644 --- a/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java @@ -1,8 +1,10 @@ package net.knarcraft.stargate.listener; +import net.knarcraft.knarlib.formatting.FormatBuilder; import net.knarcraft.knarlib.util.UpdateChecker; import net.knarcraft.stargate.Stargate; -import net.knarcraft.stargate.config.MessageSender; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalActivator; @@ -35,9 +37,15 @@ import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; +import org.geysermc.floodgate.api.FloodgateApi; +import org.geysermc.geyser.api.GeyserApi; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * This listener listens to any player-related events related to stargates @@ -46,6 +54,8 @@ import java.util.Map; public class PlayerEventListener implements Listener { private static final Map previousEventTimes = new HashMap<>(); + private boolean hasGeyser = true; + private boolean hasFloodgate = true; /** * This event handler handles detection of any player teleporting through a bungee gate @@ -53,7 +63,7 @@ public class PlayerEventListener implements Listener { * @param event

The event to check for a teleporting player

*/ @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { + public void onPlayerJoin(@NotNull PlayerJoinEvent event) { Player player = event.getPlayer(); //Migrate player name to UUID if necessary UUIDMigrationHelper.migrateUUID(player); @@ -63,7 +73,7 @@ public class PlayerEventListener implements Listener { if (availableUpdate != null && Stargate.getStargateConfig().alertAdminsAboutUpdates() && player.hasPermission("stargate.admin")) { String updateMessage = UpdateChecker.getUpdateAvailableString(availableUpdate, Stargate.getPluginVersion()); - Stargate.getMessageSender().sendErrorMessage(player, updateMessage); + new SGFormatBuilder(updateMessage).error(player); } if (!Stargate.getGateConfig().enableBungee()) { @@ -73,6 +83,7 @@ public class PlayerEventListener implements Listener { //Check if the player is waiting to be teleported to a stargate String destination = BungeeHelper.removeFromQueue(player.getUniqueId()); if (destination == null) { + Stargate.debug("PlayerJoin", "No bungee request found in queue"); return; } @@ -91,7 +102,7 @@ public class PlayerEventListener implements Listener { * @param event

The player move event which was triggered

*/ @EventHandler - public void onPlayerMove(PlayerMoveEvent event) { + public void onPlayerMove(@NotNull PlayerMoveEvent event) { if (event.isCancelled() || event.getTo() == null) { return; } @@ -108,9 +119,16 @@ public class PlayerEventListener implements Listener { //Check an additional block away in case the portal is a bungee portal using END_PORTAL if (entrancePortal == null) { entrancePortal = PortalHandler.getByAdjacentEntrance(toLocation); + // This should never realistically be null + if (entrancePortal == null) { + return; + } } Portal destination = entrancePortal.getPortalActivator().getDestination(player); + if (destination == null) { + return; + } Entity playerVehicle = player.getVehicle(); //If the player is in a vehicle, but vehicle handling is disabled, just ignore the player @@ -129,8 +147,8 @@ public class PlayerEventListener implements Listener { * @param destination

The destination of the entrance portal

* @param event

The move event causing the teleportation to trigger

*/ - private void teleportPlayer(Entity playerVehicle, Player player, Portal entrancePortal, Portal destination, - PlayerMoveEvent event) { + private void teleportPlayer(@Nullable Entity playerVehicle, @NotNull Player player, @NotNull Portal entrancePortal, + @NotNull Portal destination, @NotNull PlayerMoveEvent event) { if (playerVehicle instanceof LivingEntity) { //Make sure any horses are properly tamed if (playerVehicle instanceof AbstractHorse horse && !horse.isTamed()) { @@ -144,8 +162,8 @@ public class PlayerEventListener implements Listener { //Just teleport the player like normal new PlayerTeleporter(destination, player).teleportPlayer(entrancePortal, event); } - if (!entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg")); + if (!entrancePortal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.TELEPORTED).success(player); } entrancePortal.getPortalOpener().closePortal(false); } @@ -159,22 +177,17 @@ public class PlayerEventListener implements Listener { * @param toLocation

The location the player is moving to

* @return

True if the event is relevant

*/ - private boolean isRelevantMoveEvent(PlayerMoveEvent event, Player player, BlockLocation fromLocation, - BlockLocation toLocation) { + private boolean isRelevantMoveEvent(@NotNull PlayerMoveEvent event, Player player, + @NotNull BlockLocation fromLocation, @NotNull BlockLocation toLocation) { //Check to see if the player moved to another block if (fromLocation.equals(toLocation)) { return false; } - //Check if the player moved from a portal - Portal entrancePortal = PortalHandler.getByEntrance(toLocation); + //Get the portal the player entered, if any + Portal entrancePortal = getEnteredPortal(toLocation, player); if (entrancePortal == null) { - //Check an additional block away for BungeeCord portals using END_PORTAL as its material - entrancePortal = PortalHandler.getByAdjacentEntrance(toLocation); - if (entrancePortal == null || !entrancePortal.getOptions().isBungee() || - entrancePortal.getGate().getPortalOpenBlock() != Material.END_PORTAL) { - return false; - } + return false; } Portal destination = entrancePortal.getPortalActivator().getDestination(player); @@ -191,8 +204,8 @@ public class PlayerEventListener implements Listener { //Decide if the user should be teleported to another bungee server if (entrancePortal.getOptions().isBungee()) { - if (BungeeHelper.bungeeTeleport(player, entrancePortal, event) && !entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg")); + if (BungeeHelper.bungeeTeleport(player, entrancePortal, event) && !entrancePortal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.TELEPORTED).success(player); } return false; } @@ -201,13 +214,87 @@ public class PlayerEventListener implements Listener { return TeleportHelper.noLeashedCreaturesPreventTeleportation(player); } + /** + * Gets the portal a player entered + * + * @param toLocation

The location the player moved to

+ * @param player

The player that moved

+ * @return

The portal the player entered, or null if no portal was entered

+ */ + private Portal getEnteredPortal(@NotNull BlockLocation toLocation, @NotNull Player player) { + Portal entrancePortal = PortalHandler.getByEntrance(toLocation); + // Return if in an entrance + if (entrancePortal != null) { + return entrancePortal; + } + + //Check an additional block away for special cases like BungeeCord portals using END_PORTAL as its material + entrancePortal = PortalHandler.getByAdjacentEntrance(toLocation); + if (entrancePortal == null) { + return null; + } + + // If END_GATEWAY and END_PORTAL cannot appear, skip further checks + Set entranceMaterials = MaterialHelper.specifiersToMaterials(entrancePortal.getGate().getPortalOpenMaterials()); + if (!entranceMaterials.contains(Material.END_GATEWAY) && !entranceMaterials.contains(Material.END_PORTAL)) { + return null; + } + + // Get the real materials in the entrance, as END_GATEWAY or END_PORTAL may be available, but not chosen + Set materialsInEntrance = new HashSet<>(); + for (BlockLocation location : entrancePortal.getStructure().getEntrances()) { + materialsInEntrance.add(location.getType()); + } + + // Abort if not a special case + if ((!materialsInEntrance.contains(Material.END_GATEWAY) || !isGeyserPlayer(player)) && + (!entrancePortal.getOptions().isBungee() || !materialsInEntrance.contains(Material.END_PORTAL))) { + return null; + } + + return entrancePortal; + } + + /** + * Checks whether the given player is connected through Geyser + * + * @param player

The player to check

+ * @return

True if the player is connected through Geyser

+ */ + private boolean isGeyserPlayer(@NotNull Player player) { + // Prevent unnecessary checking for non-geyser and floodgate servers + if (!hasGeyser && !hasFloodgate) { + return false; + } + + // Use Geyser API to get connection status + if (hasGeyser) { + try { + return GeyserApi.api().connectionByUuid(player.getUniqueId()) != null; + } catch (NoClassDefFoundError error1) { + hasGeyser = false; + } + } + + // Use Floodgate API to get connection status + if (hasFloodgate) { + try { + return FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId()); + } catch (NoClassDefFoundError error2) { + hasFloodgate = false; + } + } + + return false; + } + /** * This event handler detects if a player clicks a button or a sign * * @param event

The player interact event which was triggered

*/ @EventHandler - public void onPlayerInteract(PlayerInteractEvent event) { + public void onPlayerInteract(@NotNull PlayerInteractEvent event) { Player player = event.getPlayer(); Block block = event.getClickedBlock(); @@ -216,6 +303,10 @@ public class PlayerEventListener implements Listener { } if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { + if (event.getHand() == null) { + return; + } + // Handle right-click of a sign, button or other handleRightClickBlock(event, player, block, event.getHand()); } else if (event.getAction() == Action.LEFT_CLICK_BLOCK && block.getBlockData() instanceof WallSign) { //Handle left click of a wall sign @@ -231,25 +322,16 @@ public class PlayerEventListener implements Listener { * @param block

The block that was clicked

* @param leftClick

Whether the player performed a left click as opposed to a right click

*/ - private void handleSignClick(PlayerInteractEvent event, Player player, Block block, boolean leftClick) { + private void handleSignClick(@NotNull PlayerInteractEvent event, @NotNull Player player, @NotNull Block block, + boolean leftClick) { Portal portal = PortalHandler.getByBlock(block); if (portal == null) { return; } //Allow players with permissions to apply dye to signs - EquipmentSlot hand = event.getHand(); - if (hand != null && (PermissionHelper.hasPermission(player, "stargate.admin.dye") || - portal.isOwner(player))) { - ItemStack item = player.getInventory().getItem(hand); - if (item != null) { - String itemName = item.getType().toString(); - if (itemName.endsWith("DYE") || itemName.endsWith("INK_SAC")) { - event.setUseInteractedBlock(Event.Result.ALLOW); - Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), portal::drawSign, 1); - return; - } - } + if (dyeSign(event, player, portal)) { + return; } event.setUseInteractedBlock(Event.Result.DENY); @@ -279,6 +361,39 @@ public class PlayerEventListener implements Listener { } } + /** + * Tries to take care of a sign dye interaction + * + * @param event

The triggered player interaction event

+ * @param player

The involved player

+ * @param portal

The involved portal

+ * @return

True if a sign was dyed

+ */ + private boolean dyeSign(@NotNull PlayerInteractEvent event, @NotNull Player player, @NotNull Portal portal) { + EquipmentSlot hand = event.getHand(); + // Check if the player is allowed to dye the sign + if (hand == null || (!PermissionHelper.hasPermission(player, "stargate.admin.dye") && + !portal.isOwner(player))) { + return false; + } + + // Check if the player is holding an item + ItemStack item = player.getInventory().getItem(hand); + if (item == null) { + return false; + } + + String itemName = item.getType().toString(); + // Check if the player's item can be used to dye the sign + if (itemName.endsWith("DYE") || itemName.endsWith("INK_SAC")) { + event.setUseInteractedBlock(Event.Result.ALLOW); + Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), portal::drawSign, 1); + return true; + } else { + return false; + } + } + /** * Check if a player should be denied from accessing (using) a portal * @@ -286,12 +401,12 @@ public class PlayerEventListener implements Listener { * @param portal

The portal the player is trying to use

* @return

True if the player should be denied

*/ - private boolean cannotAccessPortal(Player player, Portal portal) { + private boolean cannotAccessPortal(@NotNull Player player, @NotNull Portal portal) { boolean deny = PermissionHelper.cannotAccessNetwork(player, portal.getCleanNetwork()); if (PermissionHelper.portalAccessDenied(player, portal, deny)) { - if (!portal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); + if (!portal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.ACCESS_DENIED).error(player); } return true; } @@ -306,14 +421,15 @@ public class PlayerEventListener implements Listener { * @param block

The block the player clicked

* @param hand

The hand the player used to interact with the stargate

*/ - private void handleRightClickBlock(PlayerInteractEvent event, Player player, Block block, EquipmentSlot hand) { + private void handleRightClickBlock(@NotNull PlayerInteractEvent event, @NotNull Player player, @NotNull Block block, + @NotNull EquipmentSlot hand) { if (block.getBlockData() instanceof WallSign) { handleSignClick(event, player, block, false); return; } //Prevent a double click caused by a Spigot bug - if (clickIsBug(event.getPlayer(), block)) { + if (clickIsBug(event.getPlayer())) { return; } @@ -348,32 +464,29 @@ public class PlayerEventListener implements Listener { /** * Displays information about a clicked portal * - *

This will only display portal info if the portal has no sign and is not silent.

+ *

This will only display portal info if the portal has no sign and is not quiet.

* * @param block

The clicked block

* @param player

The player that clicked the block

*/ - private void displayPortalInfo(Block block, Player player) { + private void displayPortalInfo(@NotNull Block block, @NotNull Player player) { Portal portal = PortalHandler.getByBlock(block); if (portal == null) { return; } //Display portal information as a portal without a sign does not display any - if (portal.getOptions().hasNoSign() && (!portal.getOptions().isSilent() || player.isSneaking())) { - MessageSender sender = Stargate.getMessageSender(); - sender.sendSuccessMessage(player, ChatColor.GOLD + Stargate.getString("portalInfoTitle")); - sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoName"), - "%name%", portal.getName())); - sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoDestination"), - "%destination%", portal.getDestinationName())); + if (portal.getOptions().hasNoSign() && (!portal.getOptions().isQuiet() || player.isSneaking())) { + FormatBuilder builder = new SGFormatBuilder(); + builder.append(ChatColor.GOLD).append(Message.PORTAL_INFO_TITLE).append("\n"). + append(Message.PORTAL_INFO_NAME).replace("%name%", portal.getName()).append("\n"). + append(Message.PORTAL_INFO_DESTINATION).replace("%destination%", portal.getDestinationName()).append("\n"); if (portal.getOptions().isBungee()) { - sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoServer"), - "%server%", portal.getNetwork())); + builder.append(Message.PORTAL_INFO_SERVER).replace("%server%", portal.getNetwork()); } else { - sender.sendSuccessMessage(player, Stargate.replaceVars(Stargate.getString("portalInfoNetwork"), - "%network%", portal.getNetwork())); + builder.append(Message.PORTAL_INFO_NETWORK).replace("%network%", portal.getNetwork()); } + builder.displayRaw(player); } } @@ -385,10 +498,9 @@ public class PlayerEventListener implements Listener { * clicking once the bug is fixed.

* * @param player

The player performing the right-click

- * @param block

The block to check

* @return

True if the click is a bug and should be cancelled

*/ - private boolean clickIsBug(Player player, Block block) { + private boolean clickIsBug(@NotNull Player player) { Long previousEventTime = previousEventTimes.get(player); if (previousEventTime != null && previousEventTime + 50 > System.currentTimeMillis()) { previousEventTimes.put(player, null); diff --git a/src/main/java/net/knarcraft/stargate/listener/PluginEventListener.java b/src/main/java/net/knarcraft/stargate/listener/PluginEventListener.java index 2f70de3..1981ce5 100644 --- a/src/main/java/net/knarcraft/stargate/listener/PluginEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/PluginEventListener.java @@ -1,10 +1,14 @@ package net.knarcraft.stargate.listener; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; /** * This listener listens for any plugins being enabled or disabled to catch the loading of vault @@ -19,7 +23,7 @@ public class PluginEventListener implements Listener { * * @param stargate

A reference to the stargate plugin to

*/ - public PluginEventListener(Stargate stargate) { + public PluginEventListener(@NotNull Stargate stargate) { this.stargate = stargate; } @@ -31,10 +35,13 @@ public class PluginEventListener implements Listener { * @param ignored

The actual event called. This is currently not used

*/ @EventHandler - public void onPluginEnable(PluginEnableEvent ignored) { + public void onPluginEnable(@NotNull PluginEnableEvent ignored) { if (Stargate.getEconomyConfig().setupEconomy(stargate.getServer().getPluginManager())) { - String vaultVersion = Stargate.getEconomyConfig().getVault().getDescription().getVersion(); - Stargate.logInfo(Stargate.replaceVars(Stargate.getString("vaultLoaded"), "%version%", vaultVersion)); + Plugin vault = Stargate.getEconomyConfig().getVault(); + if (vault != null) { + String vaultVersion = vault.getDescription().getVersion(); + Stargate.logInfo(new SGFormatBuilder(Message.VAULT_LOADED).replace("%version%", vaultVersion).toString()); + } } } @@ -44,7 +51,7 @@ public class PluginEventListener implements Listener { * @param event

The event caused by disabling a plugin

*/ @EventHandler - public void onPluginDisable(PluginDisableEvent event) { + public void onPluginDisable(@NotNull PluginDisableEvent event) { if (event.getPlugin().equals(Stargate.getEconomyConfig().getVault())) { Stargate.logInfo("Vault plugin lost."); } diff --git a/src/main/java/net/knarcraft/stargate/listener/PortalEventListener.java b/src/main/java/net/knarcraft/stargate/listener/PortalEventListener.java index c81640f..8cf3086 100644 --- a/src/main/java/net/knarcraft/stargate/listener/PortalEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/PortalEventListener.java @@ -17,16 +17,17 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPortalEnterEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.world.PortalCreateEvent; +import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; /** * Listens for and cancels relevant portal events */ public class PortalEventListener implements Listener { - private static final List playersFromTheEnd = new ArrayList<>(); + private static final Map playersFromTheEnd = new HashMap<>(); /** * Listens for and aborts vanilla portal creation caused by stargate creation @@ -34,7 +35,7 @@ public class PortalEventListener implements Listener { * @param event

The triggered event

*/ @EventHandler - public void onPortalCreation(PortalCreateEvent event) { + public void onPortalCreation(@NotNull PortalCreateEvent event) { if (event.isCancelled()) { return; } @@ -56,32 +57,37 @@ public class PortalEventListener implements Listener { * @param event

The triggered event

*/ @EventHandler - public void onEntityPortalEnter(EntityPortalEnterEvent event) { + public void onEntityPortalEnter(@NotNull EntityPortalEnterEvent event) { Location location = event.getLocation(); World world = location.getWorld(); Entity entity = event.getEntity(); - //Hijack normal portal teleportation if teleporting from a stargate - if (entity instanceof Player player && location.getBlock().getType() == Material.END_PORTAL && world != null && - world.getEnvironment() == World.Environment.THE_END) { - Portal portal = PortalHandler.getByAdjacentEntrance(location); - if (portal == null) { - return; - } + //Hijack normal portal teleportation if teleporting from a stargate, and teleporting from an end portal in the + // end + if (!(entity instanceof Player player) || location.getBlock().getType() != Material.END_PORTAL || + world == null || world.getEnvironment() != World.Environment.THE_END) { + return; + } + + Portal portal = PortalHandler.getByAdjacentEntrance(location); + if (portal == null) { + return; + } + + Stargate.debug("PortalEventListener::onEntityPortalEnter", + "Found player " + player + " entering END_PORTAL " + portal); + + //Decide if the anything stops the player from teleporting + if (PermissionHelper.playerCannotTeleport(portal, portal.getPortalActivator().getDestination(), + player, null) || portal.getOptions().isBungee()) { + //Teleport the player back to the portal they came in, just in case + playersFromTheEnd.put(player, new FromTheEndTeleportation(portal)); Stargate.debug("PortalEventListener::onEntityPortalEnter", - "Found player " + player + " entering END_PORTAL " + portal); - - //Remove any old player teleportations in case weird things happen - playersFromTheEnd.removeIf((teleportation -> teleportation.getPlayer() == player)); - //Decide if the anything stops the player from teleporting - if (PermissionHelper.playerCannotTeleport(portal, portal.getPortalActivator().getDestination(), player, null) || - portal.getOptions().isBungee()) { - //Teleport the player back to the portal they came in, just in case - playersFromTheEnd.add(new FromTheEndTeleportation(player, portal)); - Stargate.debug("PortalEventListener::onEntityPortalEnter", - "Sending player back to the entrance"); - } else { - playersFromTheEnd.add(new FromTheEndTeleportation(player, portal.getPortalActivator().getDestination())); + "Sending player back to the entrance"); + } else { + Portal destination = portal.getPortalActivator().getDestination(); + if (destination != null) { + playersFromTheEnd.put(player, new FromTheEndTeleportation(destination)); Stargate.debug("PortalEventListener::onEntityPortalEnter", "Sending player to destination"); } @@ -94,16 +100,14 @@ public class PortalEventListener implements Listener { * @param event

The triggered event

*/ @EventHandler - public void onRespawn(PlayerRespawnEvent event) { + public void onRespawn(@NotNull PlayerRespawnEvent event) { Player respawningPlayer = event.getPlayer(); - int playerIndex = playersFromTheEnd.indexOf(new FromTheEndTeleportation(respawningPlayer, null)); - if (playerIndex == -1) { + FromTheEndTeleportation teleportation = playersFromTheEnd.remove(respawningPlayer); + if (teleportation == null) { return; } - FromTheEndTeleportation teleportation = playersFromTheEnd.get(playerIndex); - playersFromTheEnd.remove(playerIndex); + Portal exitPortal = teleportation.exitPortal(); - Portal exitPortal = teleportation.getExit(); //Overwrite respawn location to respawn in front of the portal PlayerTeleporter teleporter = new PlayerTeleporter(exitPortal, respawningPlayer); Location respawnLocation = teleporter.getExit(); diff --git a/src/main/java/net/knarcraft/stargate/listener/TeleportEventListener.java b/src/main/java/net/knarcraft/stargate/listener/TeleportEventListener.java index 21a0118..34ab7a6 100644 --- a/src/main/java/net/knarcraft/stargate/listener/TeleportEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/TeleportEventListener.java @@ -4,6 +4,7 @@ import net.knarcraft.stargate.portal.PortalHandler; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerTeleportEvent; +import org.jetbrains.annotations.NotNull; /** * This listener listens to teleportation-related events @@ -21,7 +22,7 @@ public class TeleportEventListener implements Listener { * @param event

The event to check and possibly cancel

*/ @EventHandler - public void onPlayerTeleport(PlayerTeleportEvent event) { + public void onPlayerTeleport(@NotNull PlayerTeleportEvent event) { PlayerTeleportEvent.TeleportCause cause = event.getCause(); //Block normal portal teleportation if teleporting from a stargate diff --git a/src/main/java/net/knarcraft/stargate/listener/VehicleEventListener.java b/src/main/java/net/knarcraft/stargate/listener/VehicleEventListener.java index 42c1102..442e1e2 100644 --- a/src/main/java/net/knarcraft/stargate/listener/VehicleEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/VehicleEventListener.java @@ -1,6 +1,8 @@ package net.knarcraft.stargate.listener; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter; @@ -13,6 +15,8 @@ import org.bukkit.entity.Vehicle; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.vehicle.VehicleMoveEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -28,7 +32,7 @@ public class VehicleEventListener implements Listener { * @param event

The triggered move event

*/ @EventHandler - public void onVehicleMove(VehicleMoveEvent event) { + public void onVehicleMove(@NotNull VehicleMoveEvent event) { if (!Stargate.getGateConfig().handleVehicles()) { return; } @@ -58,7 +62,8 @@ public class VehicleEventListener implements Listener { * @param entrancePortal

The portal the vehicle is entering

* @param vehicle

The vehicle passing through

*/ - private static void teleportVehicle(List passengers, Portal entrancePortal, Vehicle vehicle) { + private static void teleportVehicle(@NotNull List passengers, @NotNull Portal entrancePortal, + @NotNull Vehicle vehicle) { String route = "VehicleEventListener::teleportVehicle"; if (!passengers.isEmpty() && TeleportHelper.containsPlayer(passengers)) { @@ -83,14 +88,72 @@ public class VehicleEventListener implements Listener { * @param entrancePortal

The portal the minecart entered

* @param vehicle

The vehicle to teleport

*/ - private static void teleportPlayerAndVehicle(Portal entrancePortal, Vehicle vehicle) { + private static void teleportPlayerAndVehicle(@NotNull Portal entrancePortal, @NotNull Vehicle vehicle) { Entity rootEntity = vehicle; while (rootEntity.getVehicle() != null) { rootEntity = rootEntity.getVehicle(); } List players = TeleportHelper.getPlayers(rootEntity.getPassengers()); - Portal destinationPortal = null; + Portal destinationPortal = getDestinationPortal(players, entrancePortal); + //Cancel the teleport if no players activated the portal, or if any players are denied access + boolean cancelTeleportation = false; + for (Player player : players) { + if (destinationPortal == null) { + cancelTeleportation = true; + if (!entrancePortal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.INVALID_DESTINATION).error(player); + } + } else if (!TeleportHelper.playerCanTeleport(player, entrancePortal, destinationPortal)) { + cancelTeleportation = true; + } + } + if (cancelTeleportation || destinationPortal == null) { + return; + } + + //Take payment from all players + if (!takePayment(players, entrancePortal, destinationPortal)) { + return; + } + + // Perform the teleportation + teleportPlayerAndVehicle(players, vehicle, entrancePortal, destinationPortal); + } + + /** + * Performs the teleportation of one or more players in a vehicle + * + * @param players

The players to be teleported

+ * @param vehicle

The vehicle that triggered the teleportation

+ * @param entrancePortal

The portal the player(s) and vehicle entered from

+ * @param destinationPortal

The portal the player(s) and vehicle are teleporting to

+ */ + private static void teleportPlayerAndVehicle(@NotNull List players, @NotNull Vehicle vehicle, + @NotNull Portal entrancePortal, @NotNull Portal destinationPortal) { + //Teleport the vehicle and inform the user if the vehicle was teleported + boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleportEntity(entrancePortal); + if (!teleported) { + return; + } + + if (!entrancePortal.getOptions().isQuiet()) { + for (Player player : players) { + new SGFormatBuilder(Message.TELEPORTED).success(player); + } + } + entrancePortal.getPortalOpener().closePortal(false); + } + + /** + * Tries to get the destination portal selected by one of the players included in the teleportation + * + * @param players

The players to be teleported

+ * @param entrancePortal

The portal the players are entering

+ * @return

The destination portal, or null if not found

+ */ + @Nullable + private static Portal getDestinationPortal(@NotNull List players, @NotNull Portal entrancePortal) { for (Player player : players) { //The entrance portal must be open for one player for the teleportation to happen if (!entrancePortal.getPortalOpener().isOpenFor(player)) { @@ -100,48 +163,36 @@ public class VehicleEventListener implements Listener { //Check if any of the players has selected the destination Portal possibleDestinationPortal = entrancePortal.getPortalActivator().getDestination(player); if (possibleDestinationPortal != null) { - destinationPortal = possibleDestinationPortal; + return possibleDestinationPortal; } } - //Cancel the teleport if no players activated the portal, or if any players are denied access - boolean cancelTeleport = false; - for (Player player : players) { - if (destinationPortal == null) { - cancelTeleport = true; - if (!entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("invalidMsg")); - } - } else if (!TeleportHelper.playerCanTeleport(player, entrancePortal, destinationPortal)) { - cancelTeleport = true; - } - } - if (cancelTeleport) { - return; - } + return null; + } - //Take payment from all players + /** + * Takes payment for the given players + * + * @param players

The players to take payment from

+ * @param entrancePortal

The portal the players are travelling from

+ * @param destinationPortal

The portal the players are travelling to

+ * @return

True if payment was successfully taken, false otherwise

+ */ + private static boolean takePayment(@NotNull List players, @NotNull Portal entrancePortal, + @NotNull Portal destinationPortal) { for (Player player : players) { //To prevent the case where the first passenger pays and then the second passenger is denied, this has to be - // run after it has been confirmed that all passengers are able to pay + // run after it has been confirmed that all passengers are able to pay. Also note that some players might + // not have to pay, and thus the cost check has to be in the loop, int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal); if (cost > 0) { if (EconomyHelper.cannotPayTeleportFee(entrancePortal, player, cost)) { - return; + return false; } } } - //Teleport the vehicle and inform the user if the vehicle was teleported - boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleportEntity(entrancePortal); - if (teleported) { - if (!entrancePortal.getOptions().isSilent()) { - for (Player player : players) { - Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg")); - } - } - entrancePortal.getPortalOpener().closePortal(false); - } + return true; } } diff --git a/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java b/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java index 6329d6d..5606e80 100644 --- a/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java @@ -9,6 +9,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; +import org.jetbrains.annotations.NotNull; /** * This listener listens for the loading and unloading of worlds to load and unload stargates @@ -22,7 +23,7 @@ public class WorldEventListener implements Listener { * @param event

The triggered world load event

*/ @EventHandler - public void onWorldLoad(WorldLoadEvent event) { + public void onWorldLoad(@NotNull WorldLoadEvent event) { StargateConfig config = Stargate.getStargateConfig(); if (!config.getManagedWorlds().contains(event.getWorld().getName()) && PortalFileHelper.loadAllPortals(event.getWorld())) { @@ -36,7 +37,7 @@ public class WorldEventListener implements Listener { * @param event

The triggered world unload event

*/ @EventHandler - public void onWorldUnload(WorldUnloadEvent event) { + public void onWorldUnload(@NotNull WorldUnloadEvent event) { Stargate.debug("onWorldUnload", "Reloading all Stargates"); World world = event.getWorld(); String worldName = world.getName(); diff --git a/src/main/java/net/knarcraft/stargate/portal/Portal.java b/src/main/java/net/knarcraft/stargate/portal/Portal.java index 6c7db86..e1090e0 100644 --- a/src/main/java/net/knarcraft/stargate/portal/Portal.java +++ b/src/main/java/net/knarcraft/stargate/portal/Portal.java @@ -7,12 +7,15 @@ import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.portal.property.PortalOption; import net.knarcraft.stargate.portal.property.PortalOptions; import net.knarcraft.stargate.portal.property.PortalOwner; +import net.knarcraft.stargate.portal.property.PortalStrings; import net.knarcraft.stargate.portal.property.PortalStructure; import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.utility.DirectionHelper; import net.md_5.bungee.api.ChatColor; import org.bukkit.World; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Map; @@ -42,22 +45,21 @@ public class Portal { * * @param portalLocation

Object containing locations of all relevant blocks

* @param button

The location of the portal's open button

- * @param destination

The destination defined on the sign's destination line. "" for non-fixed gates

- * @param name

The name of the portal defined on the sign's first line

- * @param network

The network the portal belongs to, defined on the sign's third

+ * @param portalStrings

The portal's string values, such as name, network and destination

* @param gate

The gate type to use for this portal

* @param portalOwner

The portal's owner

* @param options

A map containing all possible portal options, with true for the ones enabled

*/ - public Portal(PortalLocation portalLocation, BlockLocation button, String destination, String name, String network, - Gate gate, PortalOwner portalOwner, Map options) { + public Portal(@NotNull PortalLocation portalLocation, @Nullable BlockLocation button, + @NotNull PortalStrings portalStrings, @NotNull Gate gate, @NotNull PortalOwner portalOwner, + @NotNull Map options) { this.location = portalLocation; - this.network = network; - this.name = name; + this.network = portalStrings.network(); + this.name = portalStrings.name(); this.portalOwner = portalOwner; - this.options = new PortalOptions(options, destination.length() > 0); + this.options = new PortalOptions(options, !portalStrings.destination().isEmpty()); this.signDrawer = new PortalSignDrawer(this); - this.portalOpener = new PortalOpener(this, destination); + this.portalOpener = new PortalOpener(this, portalStrings.destination()); this.structure = new PortalStructure(this, gate, button); this.portalActivator = portalOpener.getPortalActivator(); this.cleanName = cleanString(name); @@ -88,6 +90,7 @@ public class Portal { * * @return

This portal's location data

*/ + @NotNull public PortalLocation getLocation() { return this.location; } @@ -100,6 +103,7 @@ public class Portal { * * @return

This portal's structure

*/ + @NotNull public PortalStructure getStructure() { return this.structure; } @@ -112,6 +116,7 @@ public class Portal { * * @return

This portal's activator

*/ + @NotNull public PortalActivator getPortalActivator() { return this.portalActivator; } @@ -128,6 +133,7 @@ public class Portal { * * @return

This portal's portal options

*/ + @NotNull public PortalOptions getOptions() { return this.options; } @@ -146,6 +152,7 @@ public class Portal { * * @return

The player currently using this portal

*/ + @Nullable public Player getActivePlayer() { return portalActivator.getActivePlayer(); } @@ -155,6 +162,7 @@ public class Portal { * * @return

The network this portal belongs to

*/ + @NotNull public String getNetwork() { return network; } @@ -164,6 +172,7 @@ public class Portal { * * @return

The clean network name

*/ + @NotNull public String getCleanNetwork() { return cleanNetwork; } @@ -185,6 +194,7 @@ public class Portal { * * @return

The name of this portal

*/ + @NotNull public String getName() { return name; } @@ -194,6 +204,7 @@ public class Portal { * * @return

The clean name of this portal

*/ + @NotNull public String getCleanName() { return cleanName; } @@ -205,6 +216,7 @@ public class Portal { * * @return

This portal's portal opener

*/ + @NotNull public PortalOpener getPortalOpener() { return portalOpener; } @@ -214,6 +226,7 @@ public class Portal { * * @return

The name of this portal's destination portal

*/ + @NotNull public String getDestinationName() { return portalOpener.getPortalActivator().getDestinationName(); } @@ -223,6 +236,7 @@ public class Portal { * * @return

The gate type used by this portal

*/ + @NotNull public Gate getGate() { return structure.getGate(); } @@ -234,6 +248,7 @@ public class Portal { * * @return

This portal's owner

*/ + @NotNull public PortalOwner getOwner() { return portalOwner; } @@ -244,7 +259,7 @@ public class Portal { * @param player

The player to check

* @return

True if the player is the owner of this portal

*/ - public boolean isOwner(Player player) { + public boolean isOwner(@NotNull Player player) { if (this.portalOwner.getUUID() != null) { return player.getUniqueId().compareTo(this.portalOwner.getUUID()) == 0; } else { @@ -257,6 +272,7 @@ public class Portal { * * @return

The world this portal belongs to

*/ + @Nullable public World getWorld() { return location.getWorld(); } @@ -266,6 +282,7 @@ public class Portal { * * @return

The location of this portal's sign

*/ + @NotNull public BlockLocation getSignLocation() { return this.location.getSignLocation(); } @@ -287,6 +304,7 @@ public class Portal { * * @return

The location of the top-left portal block

*/ + @NotNull public BlockLocation getTopLeft() { return this.location.getTopLeft(); } @@ -297,7 +315,8 @@ public class Portal { * @param vector

The relative block vector explaining the position of the block

* @return

The block at the given relative position

*/ - public BlockLocation getBlockAt(RelativeBlockVector vector) { + @NotNull + public BlockLocation getBlockAt(@NotNull RelativeBlockVector vector) { return (BlockLocation) getTopLeft().clone().add(vectorOperation.performToRealSpaceOperation(vector.toVector())); } @@ -307,11 +326,13 @@ public class Portal { * @param string

The string to clean

* @return

The clean string

*/ - public static String cleanString(String string) { + @NotNull + public static String cleanString(@NotNull String string) { return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', string)).toLowerCase(); } @Override + @NotNull public String toString() { return String.format("Portal [id=%s, network=%s name=%s, type=%s]", getSignLocation(), network, name, structure.getGate().getFilename()); @@ -327,7 +348,7 @@ public class Portal { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (this == object) { return true; } diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalActivator.java b/src/main/java/net/knarcraft/stargate/portal/PortalActivator.java index c0fd01e..ed3fd74 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalActivator.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalActivator.java @@ -1,14 +1,18 @@ package net.knarcraft.stargate.portal; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.event.StargateActivateEvent; import net.knarcraft.stargate.event.StargateDeactivateEvent; +import net.knarcraft.stargate.utility.ListHelper; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Random; /** * The portal activator activates/de-activates portals and keeps track of a portal's destinations @@ -33,7 +37,7 @@ public class PortalActivator { * @param portalOpener

The portal opener to trigger when the activation causes the portal to open

* @param destination

The fixed destination specified on the portal's sign

*/ - public PortalActivator(Portal portal, PortalOpener portalOpener, String destination) { + public PortalActivator(@NotNull Portal portal, @NotNull PortalOpener portalOpener, @NotNull String destination) { this.portal = portal; this.opener = portalOpener; this.destination = destination; @@ -44,6 +48,7 @@ public class PortalActivator { * * @return

The player this activator's portal is currently activated for

*/ + @NotNull public Player getActivePlayer() { return activePlayer; } @@ -53,6 +58,7 @@ public class PortalActivator { * * @return

The available portal destinations

*/ + @NotNull public List getDestinations() { return new ArrayList<>(this.destinations); } @@ -63,20 +69,21 @@ public class PortalActivator { * @param player

Used for random gates to determine which destinations are available

* @return

The destination portal the player should teleport to

*/ - public Portal getDestination(Player player) { + @Nullable + public Portal getDestination(@Nullable Player player) { String portalNetwork = portal.getCleanNetwork(); if (portal.getOptions().isRandom()) { //Find possible destinations List destinations = PortalHandler.getDestinations(portal, player, portalNetwork); - if (destinations.size() == 0) { + if (destinations.isEmpty()) { return null; } //Get one random destination - String destination = destinations.get((new Random()).nextInt(destinations.size())); - return PortalHandler.getByName(Portal.cleanString(destination), portalNetwork); + String randomDestination = ListHelper.getRandom(destinations); + return PortalHandler.getByName(randomDestination, portalNetwork); } else { //Just return the normal fixed destination - return PortalHandler.getByName(Portal.cleanString(destination), portalNetwork); + return PortalHandler.getByName(destination, portalNetwork); } } @@ -88,6 +95,7 @@ public class PortalActivator { * * @return

The portal destination

*/ + @Nullable public Portal getDestination() { return getDestination(null); } @@ -97,7 +105,7 @@ public class PortalActivator { * * @param destination

The new destination of this portal activator's portal

*/ - public void setDestination(Portal destination) { + public void setDestination(@NotNull Portal destination) { setDestination(destination.getName()); } @@ -106,7 +114,7 @@ public class PortalActivator { * * @param destination

The new destination of this portal activator's portal

*/ - public void setDestination(String destination) { + public void setDestination(@NotNull String destination) { this.destination = destination; } @@ -115,6 +123,7 @@ public class PortalActivator { * * @return

The name of the selected destination

*/ + @NotNull public String getDestinationName() { return destination; } @@ -125,7 +134,7 @@ public class PortalActivator { * @param player

The player to activate the portal for

* @return

True if the portal was activated

*/ - boolean activate(Player player) { + public boolean activate(@NotNull Player player) { //Clear previous destination data this.destination = ""; this.destinations.clear(); @@ -162,7 +171,7 @@ public class PortalActivator { * @param player

The player trying to activate this activator's portal

* @return

True if the portal was activated. False otherwise

*/ - private boolean triggerStargateActivationEvent(Player player) { + private boolean triggerStargateActivationEvent(@NotNull Player player) { StargateActivateEvent event = new StargateActivateEvent(portal, player, destinations, destination); Stargate.getInstance().getServer().getPluginManager().callEvent(event); if (event.isCancelled()) { @@ -209,7 +218,7 @@ public class PortalActivator { * @return

Whether this portal activator's portal is active

*/ public boolean isActive() { - return portal.getOptions().isFixed() || (destinations.size() > 0); + return portal.getOptions().isFixed() || (!destinations.isEmpty()); } /** @@ -217,7 +226,7 @@ public class PortalActivator { * * @param player

The player to cycle the gate for

*/ - public void cycleDestination(Player player) { + public void cycleDestination(@NotNull Player player) { cycleDestination(player, 1); } @@ -227,7 +236,7 @@ public class PortalActivator { * @param player

The player cycling destinations

* @param direction

The direction of the cycle (+1 for next, -1 for previous)

*/ - public void cycleDestination(Player player, int direction) { + public void cycleDestination(@NotNull Player player, int direction) { //Only allow going exactly one step in either direction if (direction != 1 && direction != -1) { throw new IllegalArgumentException("The destination direction must be 1 or -1."); @@ -241,15 +250,17 @@ public class PortalActivator { } activate = true; - Stargate.debug("cycleDestination", "Network Size: " + - PortalHandler.getNetwork(portal.getCleanNetwork()).size()); + List portalsInNetwork = PortalHandler.getNetwork(portal.getCleanNetwork()); + if (portalsInNetwork != null) { + Stargate.debug("cycleDestination", "Network Size: " + portalsInNetwork.size()); + } Stargate.debug("cycleDestination", "Player has access to: " + destinations.size()); } //If no destinations are available, just tell the player and quit - if (destinations.size() == 0) { - if (!portal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("destEmpty")); + if (destinations.isEmpty()) { + if (!portal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.NO_DESTINATION).error(player); } return; } diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalCreator.java b/src/main/java/net/knarcraft/stargate/portal/PortalCreator.java index 4ec865c..e8ab252 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalCreator.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalCreator.java @@ -1,6 +1,8 @@ package net.knarcraft.stargate.portal; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.event.StargateCreateEvent; @@ -8,16 +10,22 @@ import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.portal.property.PortalOption; import net.knarcraft.stargate.portal.property.PortalOptions; import net.knarcraft.stargate.portal.property.PortalOwner; +import net.knarcraft.stargate.portal.property.PortalStrings; import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.GateHandler; import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.EconomyHelper; +import net.knarcraft.stargate.utility.ListHelper; +import net.knarcraft.stargate.utility.MaterialHelper; import net.knarcraft.stargate.utility.PermissionHelper; import net.knarcraft.stargate.utility.PortalFileHelper; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.block.SignChangeEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; @@ -39,7 +47,7 @@ public class PortalCreator { * @param event

The sign change event which initialized the creation

* @param player

The player creating the portal

*/ - public PortalCreator(SignChangeEvent event, Player player) { + public PortalCreator(@NotNull SignChangeEvent event, @NotNull Player player) { this.event = event; this.player = player; } @@ -49,27 +57,31 @@ public class PortalCreator { * * @return

The created portal

*/ + @Nullable public Portal createPortal() { + String route = "PortalCreator::createPortal"; BlockLocation signLocation = new BlockLocation(event.getBlock()); Block signControlBlock = signLocation.getParent(); //Return early if the sign is not placed on a block, or the block is not a control block - if (signControlBlock == null || GateHandler.getGatesByControlBlock(signControlBlock).length == 0) { - Stargate.debug("createPortal", "Control block not registered"); + if (signControlBlock == null || GateHandler.getGatesByControlBlock(signControlBlock).isEmpty()) { + Stargate.debug(route, "Control block not registered"); return null; } //The control block is already part of another portal if (PortalHandler.getByBlock(signControlBlock) != null) { - Stargate.debug("createPortal", "idParent belongs to existing stargate"); + Stargate.debug(route, "idParent belongs to existing stargate"); return null; } //Get necessary information from the gate's sign - String portalName = PortalHandler.filterName(event.getLine(0)); - String destinationName = PortalHandler.filterName(event.getLine(1)); - String network = PortalHandler.filterName(event.getLine(2)); - String options = PortalHandler.filterName(event.getLine(3)).toLowerCase(); + @NotNull String portalName = PortalHandler.filterName(event.getLine(0)); + @NotNull String destinationName = PortalHandler.filterName(event.getLine(1)); + @NotNull String network = PortalHandler.filterName(event.getLine(2)); + @NotNull String options = PortalHandler.filterName(event.getLine(3)).toLowerCase(); + + PortalStrings portalStrings = new PortalStrings(portalName, network, destinationName); //Get portal options available to the player creating the portal Map portalOptions = PortalHandler.getPortalOptions(player, destinationName, options); @@ -84,18 +96,26 @@ public class PortalCreator { PortalLocation portalLocation = new PortalLocation(); portalLocation.setButtonFacing(buttonFacing).setYaw(yaw).setSignLocation(signLocation); - Stargate.debug("createPortal", "Finished getting all portal info"); + Stargate.debug(route, "Finished getting all portal info"); + return createPortal(portalStrings, portalOptions, yaw, portalLocation); + } + + @Nullable + private Portal createPortal(@NotNull PortalStrings portalStrings, @NotNull Map portalOptions, + float yaw, @NotNull PortalLocation portalLocation) { + String route = "PortalCreator::createPortal"; //Try and find a gate matching the new portal Gate gate = PortalHandler.findMatchingGate(portalLocation, player.getWorld()); if ((gate == null) || (portalLocation.getButtonVector() == null)) { - Stargate.debug("createPortal", "Could not find matching gate layout"); + Stargate.debug(route, "Could not find matching gate layout"); return null; } //If the portal is a bungee portal and invalid, abort here - if (!PortalHandler.isValidBungeePortal(portalOptions, player, destinationName, network)) { - Stargate.debug("createPortal", "Portal is an invalid bungee portal"); + if (!PortalHandler.isValidBungeePortal(portalOptions, player, portalStrings.destination(), + portalStrings.network())) { + Stargate.debug(route, "Portal is an invalid bungee portal"); return null; } @@ -104,53 +124,29 @@ public class PortalCreator { for (PortalOption option : portalOptions.keySet()) { builder.append(option.getCharacterRepresentation()).append(" = ").append(portalOptions.get(option)).append(" "); } - Stargate.debug("createPortal", builder.toString()); - - //Use default network if a proper alternative is not set - if (!portalOptions.get(PortalOption.BUNGEE) && (network.length() < 1 || network.length() > - getMaxNameNetworkLength())) { - network = Stargate.getDefaultNetwork(); - } + Stargate.debug(route, builder.toString()); boolean deny = false; String denyMessage = ""; - //Check if the player can create portals on this network. If not, create a personal portal - if (!portalOptions.get(PortalOption.BUNGEE) && !PermissionHelper.canCreateNetworkGate(player, network)) { - Stargate.debug("createPortal", "Player doesn't have create permissions on network. Trying personal"); - if (PermissionHelper.canCreatePersonalPortal(player)) { - network = player.getName(); - if (network.length() > getMaxNameNetworkLength()) { - network = network.substring(0, getMaxNameNetworkLength()); - } - Stargate.debug("createPortal", "Creating personal portal"); - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createPersonal")); - } else { - Stargate.debug("createPortal", "Player does not have access to network"); + if (!(boolean) portalOptions.get(PortalOption.BUNGEE)) { + String networkName = getNetworkName(portalStrings); + if (networkName == null) { deny = true; - denyMessage = Stargate.getString("createNetDeny"); + denyMessage = new SGFormatBuilder(Message.CREATION_NETWORK_DENIED).toString(); + } else { + portalStrings = new PortalStrings(portalStrings.name(), networkName, portalStrings.destination()); } } - //Check if the player can create this gate layout - String gateName = gate.getFilename(); - gateName = gateName.substring(0, gateName.indexOf('.')); - if (!deny && !PermissionHelper.canCreatePortal(player, gateName)) { - Stargate.debug("createPortal", "Player does not have access to gate layout"); - deny = true; - denyMessage = Stargate.getString("createGateDeny"); - } - - //Check if the user can create portals to this world. - if (!portalOptions.get(PortalOption.BUNGEE) && !deny && destinationName.length() > 0) { - Portal portal = PortalHandler.getByName(destinationName, network); - if (portal != null) { - String world = portal.getWorld().getName(); - if (PermissionHelper.cannotAccessWorld(player, world)) { - Stargate.debug("canCreateNetworkGate", "Player does not have access to destination world"); - deny = true; - denyMessage = Stargate.getString("createWorldDeny"); - } + // Check whether the player can create a portal with the specified gate in the specified world + if (!deny) { + denyMessage = canCreatePortal(portalOptions.get(PortalOption.BUNGEE), portalStrings.network(), gate, + portalStrings.destination()); + if (denyMessage != null) { + deny = true; + } else { + denyMessage = ""; } } @@ -160,11 +156,72 @@ public class PortalCreator { } PortalOwner owner = new PortalOwner(player); - this.portal = new Portal(portalLocation, null, destinationName, portalName, network, gate, owner, - portalOptions); + this.portal = new Portal(portalLocation, null, portalStrings, gate, owner, portalOptions); return validatePortal(denyMessage, event.getLines(), deny); } + /** + * Gets the network name to use for the new portal + * + * @param portalStrings

The string values for the new portal

+ * @return

The new network name, or null if the player does not have the necessary permission for any networks

+ */ + @Nullable + private String getNetworkName(@NotNull PortalStrings portalStrings) { + String network = portalStrings.network(); + String route = "PortalCreator::getNetworkName"; + + //Use default network if a proper alternative is not set + if (portalStrings.network().isEmpty() || portalStrings.network().length() > getMaxNameNetworkLength()) { + network = Stargate.getDefaultNetwork(); + } + + //Check if the player can create portals on this network. If not, create a personal portal + if (!PermissionHelper.canCreateNetworkGate(player, network)) { + Stargate.debug(route, "Player doesn't have create permissions on network. Trying personal"); + if (PermissionHelper.canCreatePersonalPortal(player)) { + network = player.getName(); + if (network.length() > getMaxNameNetworkLength()) { + network = network.substring(0, getMaxNameNetworkLength()); + } + Stargate.debug(route, "Creating personal portal"); + new SGFormatBuilder(Message.CREATION_PERSONAL).error(player); + return network; + } else { + Stargate.debug(route, "Player does not have access to network"); + return null; + } + } + + return network; + } + + @Nullable + private String canCreatePortal(boolean bungee, @NotNull String network, + @NotNull Gate gate, @NotNull String destinationName) { + //Check if the player can create this gate layout + String gateName = gate.getFilename(); + gateName = gateName.substring(0, gateName.indexOf('.')); + if (!PermissionHelper.canCreatePortal(player, gateName)) { + Stargate.debug("PortalCreator::canCreatePortal", "Player does not have access to gate layout"); + return new SGFormatBuilder(Message.CREATION_GATE_DENIED).toString(); + } + + //Check if the user can create portals to this world. + if (!bungee && !destinationName.isEmpty()) { + Portal destinationPortal = PortalHandler.getByName(destinationName, network); + if (destinationPortal != null && destinationPortal.getWorld() != null) { + String world = destinationPortal.getWorld().getName(); + if (PermissionHelper.cannotAccessWorld(player, world)) { + Stargate.debug("PortalCreator::canCreatePortal", "Player does not have access to destination world"); + return new SGFormatBuilder(Message.CREATION_WORLD_DENIED).toString(); + } + } + } + + return null; + } + /** * Validates the newly created portal assigned to this portal validator * @@ -173,7 +230,8 @@ public class PortalCreator { * @param deny

Whether the portal creation has already been denied

* @return

The portal or null if its creation was denied

*/ - public Portal validatePortal(String denyMessage, String[] lines, boolean deny) { + @Nullable + public Portal validatePortal(@NotNull String denyMessage, @NotNull String[] lines, boolean deny) { PortalLocation portalLocation = portal.getLocation(); Gate gate = portal.getStructure().getGate(); PortalOptions portalOptions = portal.getOptions(); @@ -193,7 +251,7 @@ public class PortalCreator { //Tell the user why it was denied from creating the portal if (stargateCreateEvent.getDeny()) { if (!stargateCreateEvent.getDenyReason().trim().isEmpty()) { - Stargate.getMessageSender().sendErrorMessage(player, stargateCreateEvent.getDenyReason()); + new SGFormatBuilder(stargateCreateEvent.getDenyReason()).error(player); } return null; } @@ -219,7 +277,9 @@ public class PortalCreator { PortalHandler.updatePortalsPointingAtNewPortal(portal); } - PortalFileHelper.saveAllPortals(portal.getWorld()); + if (portal.getWorld() != null) { + PortalFileHelper.saveAllPortals(portal.getWorld()); + } return portal; } @@ -231,35 +291,37 @@ public class PortalCreator { * @param portalName

The name of the newly created portal

* @return

True if the portal is completely valid

*/ - private boolean checkIfNewPortalIsValid(int cost, String portalName) { + private boolean checkIfNewPortalIsValid(int cost, @NotNull String portalName) { + String route = "PortalCreator::checkIfNewPortalIsValid"; + //Check if the portal name can fit on the sign with padding (>name<) - if (portal.getCleanName().length() < 1 || portal.getCleanName().length() > getMaxNameNetworkLength()) { - Stargate.debug("createPortal", String.format("Name length error. %s is too long.", + if (portal.getCleanName().isEmpty() || portal.getCleanName().length() > getMaxNameNetworkLength()) { + Stargate.debug(route, String.format("Name length error. %s is too long.", portal.getCleanName())); - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createNameLength")); + new SGFormatBuilder(Message.CREATION_NAME_LENGTH).error(player); return false; } if (portal.getOptions().isBungee()) { //Check if the bungee portal's name has been duplicated - if (PortalHandler.getBungeePortals().get(portal.getCleanName()) != null) { - Stargate.debug("createPortal::Bungee", "Gate name duplicate"); - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createExists")); + if (PortalRegistry.getBungeePortal(portal.getCleanName()) != null) { + Stargate.debug(route, "Gate name duplicate"); + new SGFormatBuilder(Message.CREATION_NAME_COLLISION).error(player); return false; } } else { //Check if the portal name has been duplicated on the network if (PortalHandler.getByName(portal.getCleanName(), portal.getCleanNetwork()) != null) { - Stargate.debug("createPortal", "Gate name duplicate"); - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createExists")); + Stargate.debug(route, "Gate name duplicate"); + new SGFormatBuilder(Message.CREATION_NAME_COLLISION).error(player); return false; } //Check if the number of portals in the network has been surpassed - List networkList = PortalHandler.getAllPortalNetworks().get(portal.getCleanNetwork()); + List networkList = PortalHandler.getNetwork(portal.getCleanNetwork()); int maxGates = Stargate.getGateConfig().maxGatesEachNetwork(); if (maxGates > 0 && networkList != null && networkList.size() >= maxGates) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createFull")); + new SGFormatBuilder(Message.CREATION_NETWORK_FULL).error(player); return false; } } @@ -268,7 +330,7 @@ public class PortalCreator { //Deduct the required fee from the player if (!EconomyHelper.chargePlayerIfNecessary(player, cost)) { EconomyHelper.sendInsufficientFundsMessage(portalName, player, cost); - Stargate.debug("createPortal", "Insufficient Funds"); + Stargate.debug(route, "Insufficient Funds"); return false; } else { EconomyHelper.sendDeductMessage(portalName, player, cost); @@ -282,7 +344,7 @@ public class PortalCreator { * * @param destinationName

The name of the destination portal. Only used if set as always on

*/ - private void updateNewPortalOpenState(String destinationName) { + private void updateNewPortalOpenState(@NotNull String destinationName) { portal.drawSign(); if (portal.getOptions().isRandom() || portal.getOptions().isBungee()) { //Open the implicitly always on portal @@ -297,8 +359,12 @@ public class PortalCreator { } else { //Update the block type for the portal's opening to the closed block as the closed block can be anything, // not just air or water + @NotNull List possibleMaterials = MaterialHelper.specifiersToMaterials( + portal.getGate().getPortalClosedMaterials()).stream().toList(); + Material closedType = ListHelper.getRandom(possibleMaterials); + for (BlockLocation entrance : portal.getStructure().getEntrances()) { - entrance.setType(portal.getGate().getPortalClosedBlock()); + entrance.setType(closedType); } } } @@ -312,12 +378,14 @@ public class PortalCreator { * @param player

The player creating the new portal

* @return

True if a conflict was found. False otherwise

*/ - private static boolean conflictsWithExistingPortal(Gate gate, BlockLocation topLeft, double yaw, Player player) { + private static boolean conflictsWithExistingPortal(@NotNull Gate gate, @NotNull BlockLocation topLeft, double yaw, + @NotNull Player player) { for (RelativeBlockVector borderVector : gate.getLayout().getBorder()) { BlockLocation borderBlockLocation = topLeft.getRelativeLocation(borderVector, yaw); if (PortalHandler.getByBlock(borderBlockLocation.getBlock()) != null) { - Stargate.debug("createPortal", "Gate conflicts with existing gate"); - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("createConflict")); + Stargate.debug("PortalCreator::conflictsWithExistingPortal", + "Gate conflicts with existing gate"); + new SGFormatBuilder(Message.CREATION_CONFLICT).error(player); return true; } } diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java b/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java index 44ffb81..836af78 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java @@ -1,6 +1,9 @@ package net.knarcraft.stargate.portal; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; +import net.knarcraft.stargate.config.material.BukkitTagSpecifier; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.portal.property.PortalLocation; @@ -8,11 +11,15 @@ import net.knarcraft.stargate.portal.property.PortalOption; import net.knarcraft.stargate.portal.property.PortalStructure; import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.GateHandler; +import net.knarcraft.stargate.utility.MaterialHelper; import net.knarcraft.stargate.utility.PermissionHelper; import org.bukkit.Location; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.HashMap; @@ -33,26 +40,19 @@ public class PortalHandler { * * @return

A copy of all portal networks

*/ + @NotNull public static Map> getAllPortalNetworks() { return PortalRegistry.getAllPortalNetworks(); } - /** - * Gets a copy of all bungee portals - * - * @return

A copy of all bungee portals

- */ - public static Map getBungeePortals() { - return PortalRegistry.getBungeePortals(); - } - /** * Gets names of all portals within a network * * @param network

The network to get portals from

* @return

A list of portal names

*/ - public static List getNetwork(String network) { + @Nullable + public static List getNetwork(@NotNull String network) { return PortalRegistry.getNetwork(network); } @@ -64,53 +64,73 @@ public class PortalHandler { * @param network

The network to get destinations from

* @return

All destinations the player can go to

*/ - public static List getDestinations(Portal entrancePortal, Player player, String network) { + @NotNull + public static List getDestinations(@NotNull Portal entrancePortal, @Nullable Player player, + @NotNull String network) { List destinations = new ArrayList<>(); - for (String destination : PortalRegistry.getAllPortalNetworks().get(network)) { + List portalsInNetwork = PortalRegistry.getNetwork(network); + if (portalsInNetwork == null) { + return List.of(); + } + for (String destination : portalsInNetwork) { Portal portal = getByName(destination, network); if (portal == null) { continue; } - //Check if destination is a random portal - if (portal.getOptions().isRandom()) { - continue; - } - //Check if destination is always open (Don't show if so) - if (portal.getOptions().isAlwaysOn() && !portal.getOptions().isShown()) { - continue; - } - //Check if destination is this portal - if (destination.equals(entrancePortal.getCleanName())) { - continue; - } - //Check if destination is a fixed portal not pointing to this portal - if (portal.getOptions().isFixed() && - !Portal.cleanString(portal.getDestinationName()).equals(entrancePortal.getCleanName())) { - continue; - } - //Allow random use by non-players (Minecarts) - if (player == null) { - destinations.add(portal.getName()); - continue; - } - //Check if this player can access the destination world - if (PermissionHelper.cannotAccessWorld(player, portal.getWorld().getName())) { - continue; - } - //The portal is visible to the player - if (PermissionHelper.canSeePortal(player, portal)) { + + if (isDestinationAvailable(entrancePortal, portal, player)) { destinations.add(portal.getName()); } } return destinations; } + /** + * Checks whether the given destination is available to the given player + * + * @param entrancePortal

The portal the player has activated

+ * @param destinationPortal

The destination portal to check if is valid

+ * @param player

The player trying to attempt the destination

+ * @return

True if the destination is available

+ */ + private static boolean isDestinationAvailable(@NotNull Portal entrancePortal, @NotNull Portal destinationPortal, + @Nullable Player player) { + //Check if destination is a random portal + if (destinationPortal.getOptions().isRandom()) { + return false; + } + //Check if destination is always open (Don't show if so) + if (destinationPortal.getOptions().isAlwaysOn() && !destinationPortal.getOptions().isShown()) { + return false; + } + //Check if destination is this portal + if (destinationPortal.equals(entrancePortal)) { + return false; + } + //Check if destination is a fixed portal not pointing to this portal + if (destinationPortal.getOptions().isFixed() && + !Portal.cleanString(destinationPortal.getDestinationName()).equals(entrancePortal.getCleanName())) { + return false; + } + //Allow random use by non-players (Minecarts) + if (player == null) { + return true; + } + //Check if this player can access the destination world + if (destinationPortal.getWorld() != null && PermissionHelper.cannotAccessWorld(player, + destinationPortal.getWorld().getName())) { + return false; + } + //The portal is visible to the player + return PermissionHelper.canSeePortal(player, destinationPortal); + } + /** * Registers a portal * * @param portal

The portal to register

*/ - public static void registerPortal(Portal portal) { + public static void registerPortal(@NotNull Portal portal) { PortalRegistry.registerPortal(portal); } @@ -123,19 +143,20 @@ public class PortalHandler { * @param network

The name of the portal's network

* @return

False if the portal is an invalid bungee portal. True otherwise

*/ - static boolean isValidBungeePortal(Map portalOptions, Player player, - String destinationName, String network) { - if (portalOptions.get(PortalOption.BUNGEE)) { - if (!PermissionHelper.hasPermission(player, "stargate.admin.bungee")) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDeny")); - return false; - } else if (!Stargate.getGateConfig().enableBungee()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled")); - return false; - } else if (destinationName.isEmpty() || network.isEmpty()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeEmpty")); - return false; - } + public static boolean isValidBungeePortal(@NotNull Map portalOptions, @NotNull Player player, + @NotNull String destinationName, String network) { + if (!portalOptions.get(PortalOption.BUNGEE)) { + return true; + } + if (!PermissionHelper.hasPermission(player, "stargate.admin.bungee")) { + new SGFormatBuilder(Message.BUNGEE_CREATION_DENIED).error(player); + return false; + } else if (!Stargate.getGateConfig().enableBungee()) { + new SGFormatBuilder(Message.BUNGEE_DISABLED).error(player); + return false; + } else if (destinationName.isEmpty() || network.isEmpty()) { + new SGFormatBuilder(Message.BUNGEE_MISSING_INFO).error(player); + return false; } return true; } @@ -147,13 +168,17 @@ public class PortalHandler { * @param world

The world the player is located in

* @return

The matching gate type, or null if no such gate could be found

*/ - static Gate findMatchingGate(PortalLocation portalLocation, World world) { + @Nullable + public static Gate findMatchingGate(@NotNull PortalLocation portalLocation, @NotNull World world) { Block signParent = portalLocation.getSignLocation().getParent(); + if (signParent == null) { + return null; + } BlockLocation parent = new BlockLocation(world, signParent.getX(), signParent.getY(), signParent.getZ()); //Get all gates with the used type of control blocks - Gate[] possibleGates = GateHandler.getGatesByControlBlock(signParent); + List possibleGates = GateHandler.getGatesByControlBlock(signParent); double yaw = portalLocation.getYaw(); Gate gate = null; @@ -172,6 +197,11 @@ public class PortalHandler { portalLocation.setButtonVector(controlVector); } } + + //If our gate has been found, look no further + if (gate != null) { + break; + } } return gate; @@ -182,12 +212,16 @@ public class PortalHandler { * * @param portal

The newly created portal

*/ - static void updatePortalsPointingAtNewPortal(Portal portal) { - for (String originName : PortalRegistry.getAllPortalNetworks().get(portal.getCleanNetwork())) { + public static void updatePortalsPointingAtNewPortal(@NotNull Portal portal) { + List portalsInNetwork = PortalRegistry.getNetwork(portal.getCleanNetwork()); + if (portalsInNetwork == null) { + return; + } + for (String originName : portalsInNetwork) { Portal origin = getByName(originName, portal.getCleanNetwork()); if (origin == null || !Portal.cleanString(origin.getDestinationName()).equals(portal.getCleanName()) || - !origin.getStructure().isVerified()) { + !new BukkitTagSpecifier(Tag.WALL_SIGNS).asMaterials().contains(origin.getLocation().getSignLocation().getType())) { continue; } //Update sign of fixed gates pointing at this gate @@ -209,7 +243,9 @@ public class PortalHandler { * @param options

The string on the option line of the sign

* @return

A map containing all portal options and their values

*/ - static Map getPortalOptions(Player player, String destinationName, String options) { + @NotNull + public static Map getPortalOptions(@NotNull Player player, @NotNull String destinationName, + @NotNull String options) { Map portalOptions = new HashMap<>(); for (PortalOption option : PortalOption.values()) { portalOptions.put(option, options.indexOf(option.getCharacterRepresentation()) != -1 && @@ -217,7 +253,7 @@ public class PortalHandler { } //Can not create a non-fixed always-on portal - if (portalOptions.get(PortalOption.ALWAYS_ON) && destinationName.length() == 0) { + if (portalOptions.get(PortalOption.ALWAYS_ON) && destinationName.isEmpty()) { portalOptions.put(PortalOption.ALWAYS_ON, false); } @@ -247,13 +283,9 @@ public class PortalHandler { * @param network

The network the portal is connected to

* @return

The portal with the given name or null

*/ - public static Portal getByName(String name, String network) { - Map> lookupMap = PortalRegistry.getPortalLookupByNetwork(); - if (!lookupMap.containsKey(network.toLowerCase())) { - return null; - } - return lookupMap.get(network.toLowerCase()).get(name.toLowerCase()); - + @Nullable + public static Portal getByName(@NotNull String name, @NotNull String network) { + return PortalRegistry.getPortalInNetwork(Portal.cleanString(network), Portal.cleanString(name)); } /** @@ -262,8 +294,12 @@ public class PortalHandler { * @param location

The location of the portal's entrance

* @return

The portal at the given location

*/ - public static Portal getByEntrance(Location location) { - return PortalRegistry.getLookupEntrances().get(new BlockLocation(location.getWorld(), location.getBlockX(), + @Nullable + public static Portal getByEntrance(@NotNull Location location) { + if (location.getWorld() == null) { + return null; + } + return PortalRegistry.getPortalFromEntrance(new BlockLocation(location.getWorld(), location.getBlockX(), location.getBlockY(), location.getBlockZ())); } @@ -273,8 +309,9 @@ public class PortalHandler { * @param block

The block at the portal's entrance

* @return

The portal at the given block's location

*/ - public static Portal getByEntrance(Block block) { - return PortalRegistry.getLookupEntrances().get(new BlockLocation(block)); + @Nullable + public static Portal getByEntrance(@NotNull Block block) { + return PortalRegistry.getPortalFromEntrance(new BlockLocation(block)); } /** @@ -283,7 +320,8 @@ public class PortalHandler { * @param location

A location adjacent to the portal's entrance

* @return

The portal adjacent to the given location

*/ - public static Portal getByAdjacentEntrance(Location location) { + @Nullable + public static Portal getByAdjacentEntrance(@NotNull Location location) { return getByAdjacentEntrance(location, 1); } @@ -294,7 +332,8 @@ public class PortalHandler { * @param range

The range to scan for portals

* @return

The portal adjacent to the given location

*/ - public static Portal getByAdjacentEntrance(Location location, int range) { + @Nullable + public static Portal getByAdjacentEntrance(@NotNull Location location, int range) { List adjacentPositions = new ArrayList<>(); BlockLocation centerLocation = new BlockLocation(location.getBlock()); adjacentPositions.add(centerLocation); @@ -313,7 +352,7 @@ public class PortalHandler { } for (BlockLocation adjacentPosition : adjacentPositions) { - Portal portal = PortalRegistry.getLookupEntrances().get(adjacentPosition); + Portal portal = PortalRegistry.getPortalFromEntrance(adjacentPosition); if (portal != null) { return portal; } @@ -327,8 +366,9 @@ public class PortalHandler { * @param block

The portal's control block

* @return

The portal with the given control block

*/ - public static Portal getByControl(Block block) { - return PortalRegistry.getLookupControls().get(new BlockLocation(block)); + @Nullable + public static Portal getByControl(@NotNull Block block) { + return PortalRegistry.getPortalFromControl(new BlockLocation(block)); } /** @@ -337,8 +377,9 @@ public class PortalHandler { * @param block

One of the loaded lookup blocks

* @return

The portal corresponding to the block

*/ - public static Portal getByBlock(Block block) { - return PortalRegistry.getLookupBlocks().get(new BlockLocation(block)); + @Nullable + public static Portal getByBlock(@NotNull Block block) { + return PortalRegistry.getPortalFromFrame(new BlockLocation(block)); } /** @@ -347,8 +388,9 @@ public class PortalHandler { * @param name

The name of the bungee portal to get

* @return

A bungee portal

*/ - public static Portal getBungeePortal(String name) { - return PortalRegistry.getBungeePortals().get(name.toLowerCase()); + @Nullable + public static Portal getBungeePortal(@NotNull String name) { + return PortalRegistry.getBungeePortal(name); } /** @@ -357,7 +399,8 @@ public class PortalHandler { * @param portalData

The string list containing all information about a portal

* @return

A map between portal options and booleans

*/ - public static Map getPortalOptions(String[] portalData) { + @NotNull + public static Map getPortalOptions(@NotNull String[] portalData) { Map portalOptions = new HashMap<>(); for (PortalOption option : PortalOption.values()) { int saveIndex = option.getSaveIndex(); @@ -394,7 +437,18 @@ public class PortalHandler { for (Portal portal : PortalRegistry.getAllPortals()) { //Try and verify the portal. Invalidate it if it cannot be validated PortalStructure structure = portal.getStructure(); - if (!structure.wasVerified() && (!structure.isVerified() || !structure.checkIntegrity())) { + + Stargate.debug("PortalHandler::verifyAllPortals", "Checking portal: " + portal.getName() + " | " + portal.getNetwork()); + if (!portal.getOptions().hasNoSign() && !(new BukkitTagSpecifier(Tag.WALL_SIGNS).asMaterials().contains( + portal.getLocation().getSignLocation().getType()))) { + Stargate.debug("PortalHandler::verifyAllPortals", "Stargate is missing its sign"); + invalidPortals.add(portal); + } else if (!portal.getOptions().isAlwaysOn() && portal.getLocation().getButtonVector() != null && + !MaterialHelper.isButtonCompatible(portal.getBlockAt(portal.getLocation().getButtonVector().addOut(1)).getType())) { + Stargate.debug("PortalHandler::verifyAllPortals", "Stargate is missing a valid button"); + invalidPortals.add(portal); + } else if (!structure.checkIntegrity()) { + Stargate.debug("PortalHandler::verifyAllPortals", "Stargate's border or entrance has invalid blocks"); invalidPortals.add(portal); } } @@ -410,18 +464,19 @@ public class PortalHandler { * * @param portal

The portal of the star portal

*/ - private static void unregisterInvalidPortal(Portal portal) { + private static void unregisterInvalidPortal(@NotNull Portal portal) { //Show debug information for (RelativeBlockVector control : portal.getGate().getLayout().getControls()) { Block block = portal.getBlockAt(control).getBlock(); //Log control blocks not matching the gate layout - if (!block.getType().equals(portal.getGate().getControlBlock())) { - Stargate.debug("PortalHandler::destroyInvalidPortal", "Control Block Type == " + + if (!MaterialHelper.specifiersToMaterials(portal.getGate().getControlBlockMaterials()).contains( + block.getType())) { + Stargate.debug("PortalHandler::unregisterInvalidPortal", "Control Block Type == " + block.getType().name()); } } PortalRegistry.unregisterPortal(portal, false); - Stargate.logInfo(String.format("Destroying stargate at %s", portal)); + Stargate.logInfo(String.format("Disabled stargate at %s", portal)); } /** @@ -442,7 +497,8 @@ public class PortalHandler { * @param input

The name to filter

* @return

The filtered name

*/ - public static String filterName(String input) { + @NotNull + public static String filterName(@Nullable String input) { if (input == null) { return ""; } diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalOpener.java b/src/main/java/net/knarcraft/stargate/portal/PortalOpener.java index 8390c05..1ff461d 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalOpener.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalOpener.java @@ -1,15 +1,23 @@ package net.knarcraft.stargate.portal; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.material.BukkitTagSpecifier; import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.event.StargateCloseEvent; import net.knarcraft.stargate.event.StargateOpenEvent; import net.knarcraft.stargate.portal.property.PortalOptions; +import net.knarcraft.stargate.utility.ListHelper; +import net.knarcraft.stargate.utility.MaterialHelper; import org.bukkit.Axis; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.block.data.Orientable; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; /** * The portal opener is responsible for opening and closing a portal @@ -28,7 +36,7 @@ public class PortalOpener { * @param portal

The portal this portal opener should open

* @param destination

The fixed destination defined on the portal's sign

*/ - public PortalOpener(Portal portal, String destination) { + public PortalOpener(@NotNull Portal portal, @NotNull String destination) { this.portal = portal; this.portalActivator = new PortalActivator(portal, this, destination); } @@ -56,6 +64,7 @@ public class PortalOpener { * * @return

The portal activator belonging to this portal opener

*/ + @NotNull public PortalActivator getPortalActivator() { return this.portalActivator; } @@ -75,7 +84,7 @@ public class PortalOpener { * @param openFor

The player to open the portal for

* @param force

Whether to force the portal open, even if it's already open for some player

*/ - public void openPortal(Player openFor, boolean force) { + public void openPortal(@Nullable Player openFor, boolean force) { //Call the StargateOpenEvent to allow the opening to be cancelled StargateOpenEvent event = new StargateOpenEvent(openFor, portal, force); Stargate.getInstance().getServer().getPluginManager().callEvent(event); @@ -84,13 +93,16 @@ public class PortalOpener { } //Get the material to change the opening to - Material openType = portal.getGate().getPortalOpenBlock(); + @NotNull List possibleMaterials = MaterialHelper.specifiersToMaterials( + portal.getGate().getPortalOpenMaterials()).stream().toList(); + Material openType = ListHelper.getRandom(possibleMaterials); + //Adjust orientation if applicable Axis axis = (openType.createBlockData() instanceof Orientable) ? portal.getLocation().getRotationAxis() : null; //Change the entrance blocks to the correct type for (BlockLocation inside : portal.getStructure().getEntrances()) { - Stargate.addBlockChangeRequest(new BlockChangeRequest(inside, openType, axis)); + Stargate.addControlBlockUpdateRequest(new BlockChangeRequest(inside, openType, axis)); } //Update the portal state to make is actually open @@ -102,7 +114,7 @@ public class PortalOpener { * * @param openFor

The player to open this portal opener's portal for

*/ - private void updatePortalOpenState(Player openFor) { + private void updatePortalOpenState(@Nullable Player openFor) { //Update the open state of this portal isOpen = true; triggeredTime = System.currentTimeMillis() / 1000; @@ -134,8 +146,8 @@ public class PortalOpener { //Set the destination portal to this opener's portal destination.getPortalActivator().setDestination(portal); - //Update the destination's sign if it's verified - if (destination.getStructure().isVerified()) { + //Update the destination's sign if it exists + if (new BukkitTagSpecifier(Tag.WALL_SIGNS).asMaterials().contains(destination.getLocation().getSignLocation().getType())) { destination.drawSign(); } } @@ -165,9 +177,15 @@ public class PortalOpener { } //Close the portal by requesting the opening blocks to change - Material closedType = portal.getGate().getPortalClosedBlock(); + @NotNull List possibleMaterials = MaterialHelper.specifiersToMaterials( + portal.getGate().getPortalClosedMaterials()).stream().toList(); + Material closedType = ListHelper.getRandom(possibleMaterials); + + //Adjust orientation if applicable + Axis axis = (closedType.createBlockData() instanceof Orientable) ? portal.getLocation().getRotationAxis() : null; + for (BlockLocation entrance : portal.getStructure().getEntrances()) { - Stargate.addBlockChangeRequest(new BlockChangeRequest(entrance, closedType, null)); + Stargate.addControlBlockUpdateRequest(new BlockChangeRequest(entrance, closedType, axis)); } //Update the portal state to make it actually closed @@ -207,7 +225,7 @@ public class PortalOpener { * @param player

The player to check portal state for

* @return

True if this portal opener's portal is open to the given player

*/ - public boolean isOpenFor(Player player) { + public boolean isOpenFor(@Nullable Player player) { //If closed, it's closed for everyone if (!isOpen) { return false; diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java b/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java index fe07562..aed92cf 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java @@ -2,9 +2,13 @@ package net.knarcraft.stargate.portal; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.config.DynmapManager; +import net.knarcraft.stargate.config.material.BukkitTagSpecifier; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.utility.PortalFileHelper; +import org.bukkit.Tag; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.HashMap; @@ -44,11 +48,11 @@ public class PortalRegistry { * * @param world

The world containing the portals to clear

*/ - public static void clearPortals(World world) { + public static void clearPortals(@NotNull World world) { //Storing the portals to clear is necessary to avoid a concurrent modification exception List portalsToRemove = new ArrayList<>(); allPortals.forEach((portal) -> { - if (portal.getWorld().equals(world)) { + if (portal.getWorld() != null && portal.getWorld().equals(world)) { portalsToRemove.add(portal); } }); @@ -61,7 +65,7 @@ public class PortalRegistry { * * @param portalsToRemove

A list of portals to remove

*/ - private static void clearPortals(List portalsToRemove) { + private static void clearPortals(@NotNull List portalsToRemove) { //Store the names of the portals to remove as some maps require the name, not the object List portalNames = new ArrayList<>(); portalsToRemove.forEach((portal) -> portalNames.add(portal.getCleanName())); @@ -87,44 +91,58 @@ public class PortalRegistry { * * @return

A copy of the list of all portals

*/ + @NotNull public static List getAllPortals() { return new ArrayList<>(allPortals); } /** - * Gets a copy of the lookup map for finding a portal by its frame + * Gets a portal that the given frame block belongs to * - * @return

A copy of the frame block lookup map

+ * @param blockLocation

The location that might be a frame block

+ * @return

The portal the frame block belongs to, or null

*/ - public static Map getLookupBlocks() { - return new HashMap<>(lookupBlocks); + @Nullable + public static Portal getPortalFromFrame(@NotNull BlockLocation blockLocation) { + return lookupBlocks.get(blockLocation); } /** - * Gets a copy of the lookup map for finding a portal by its control block + * Gets the portal that the given control block belongs to * - * @return

A copy of the control block lookup map

+ * @param blockLocation

The location that might be a portal control block

+ * @return

The portal the control block belongs to, or null

*/ - public static Map getLookupControls() { - return new HashMap<>(lookupControls); + @Nullable + public static Portal getPortalFromControl(@NotNull BlockLocation blockLocation) { + return lookupControls.get(blockLocation); } /** - * Gets a copy of the lookup map for finding all portals in a network + * Gets the portal identified by the given network name and portal name * - * @return

A copy of the network portal lookup map

+ * @param networkName

The name of the network the portal belongs to

+ * @param portalName

The name of the portal

+ * @return

The portal, or null if no such network and/or portal exists

*/ - public static Map> getPortalLookupByNetwork() { - return new HashMap<>(portalLookupByNetwork); + @Nullable + public static Portal getPortalInNetwork(@NotNull String networkName, @NotNull String portalName) { + Map portalsInNetwork = portalLookupByNetwork.get(Portal.cleanString(networkName)); + if (portalsInNetwork == null) { + return null; + } + return portalsInNetwork.get(Portal.cleanString(portalName)); } /** - * Gets a copy of all portal entrances available for lookup + * Gets a portal from the location of a possible entrance * - * @return

A copy of all entrances to portal mappings

+ * @param blockLocation

A location that might be a portal's entrance

+ * @return

A portal, or null

*/ - public static Map getLookupEntrances() { - return new HashMap<>(lookupEntrances); + @Nullable + public static Portal getPortalFromEntrance(@NotNull BlockLocation blockLocation) { + return lookupEntrances.get(blockLocation); } /** @@ -132,17 +150,20 @@ public class PortalRegistry { * * @return

A copy of all portal networks

*/ + @NotNull public static Map> getAllPortalNetworks() { return new HashMap<>(allPortalNetworks); } /** - * Gets a copy of all bungee portals + * Gets the BungeeCord portal with the given name * - * @return

A copy of all bungee portals

+ * @param portalName

The name of the portal to get

+ * @return

The portal, or null

*/ - public static Map getBungeePortals() { - return new HashMap<>(bungeePortals); + @Nullable + public static Portal getBungeePortal(@NotNull String portalName) { + return bungeePortals.get(Portal.cleanString(portalName)); } /** @@ -151,7 +172,8 @@ public class PortalRegistry { * @param network

The network to get portals from

* @return

A list of portal names

*/ - public static List getNetwork(String network) { + @Nullable + public static List getNetwork(@NotNull String network) { return allPortalNetworks.get(network.toLowerCase()); } @@ -161,7 +183,7 @@ public class PortalRegistry { * @param portal

The portal to un-register

* @param removeAll

Whether to remove the portal from the list of all portals

*/ - public static void unregisterPortal(Portal portal, boolean removeAll) { + public static void unregisterPortal(@NotNull Portal portal, boolean removeAll) { Stargate.debug("Unregister", "Unregistering gate " + portal.getName()); portal.getPortalActivator().deactivate(); portal.getPortalOpener().closePortal(true); @@ -169,6 +191,51 @@ public class PortalRegistry { String portalName = portal.getCleanName(); String networkName = portal.getCleanNetwork(); + clearLookupMaps(portal, removeAll); + + if (portal.getOptions().isBungee()) { + //Remove the bungee listing + bungeePortals.remove(portalName); + } else { + //Remove from network lists + portalLookupByNetwork.get(networkName).remove(portalName); + allPortalNetworks.get(networkName).remove(portalName); + + //Update all portals in the same network with this portal as its destination + for (String originName : allPortalNetworks.get(networkName)) { + Portal origin = PortalHandler.getByName(originName, portal.getCleanNetwork()); + if (origin == null || !origin.getDestinationName().equalsIgnoreCase(portalName) || + !new BukkitTagSpecifier(Tag.WALL_SIGNS).asMaterials().contains(origin.getLocation().getSignLocation().getType())) { + continue; + } + //Update the portal's sign + if (origin.getOptions().isFixed()) { + origin.drawSign(); + } + //Close portal without destination + if (origin.getOptions().isAlwaysOn()) { + origin.getPortalOpener().closePortal(true); + } + } + } + + //Mark the portal's sign as unregistered + new PortalSignDrawer(portal).drawUnregisteredSign(); + + if (portal.getWorld() != null) { + PortalFileHelper.saveAllPortals(portal.getWorld()); + } + portal.setRegistered(false); + DynmapManager.removePortalMarker(portal); + } + + /** + * Clears the given portal's presence from lookup maps + * + * @param portal

The portal to clear

+ * @param removeAll

Whether to remove the portal from the list of all portals

+ */ + private static void clearLookupMaps(@NotNull Portal portal, boolean removeAll) { //Remove portal from lookup blocks for (BlockLocation block : portal.getStructure().getFrame()) { lookupBlocks.remove(block); @@ -193,39 +260,6 @@ public class PortalRegistry { if (removeAll) { allPortals.remove(portal); } - - if (portal.getOptions().isBungee()) { - //Remove the bungee listing - bungeePortals.remove(portalName); - } else { - //Remove from network lists - portalLookupByNetwork.get(networkName).remove(portalName); - allPortalNetworks.get(networkName).remove(portalName); - - //Update all portals in the same network with this portal as its destination - for (String originName : allPortalNetworks.get(networkName)) { - Portal origin = PortalHandler.getByName(originName, portal.getCleanNetwork()); - if (origin == null || !origin.getDestinationName().equalsIgnoreCase(portalName) || - !origin.getStructure().isVerified()) { - continue; - } - //Update the portal's sign - if (origin.getOptions().isFixed()) { - origin.drawSign(); - } - //Close portal without destination - if (origin.getOptions().isAlwaysOn()) { - origin.getPortalOpener().closePortal(true); - } - } - } - - //Mark the portal's sign as unregistered - new PortalSignDrawer(portal).drawUnregisteredSign(); - - PortalFileHelper.saveAllPortals(portal.getWorld()); - portal.setRegistered(false); - DynmapManager.removePortalMarker(portal); } /** @@ -233,8 +267,8 @@ public class PortalRegistry { * * @param portal

The portal to register

*/ - static void registerPortal(Portal portal) { - portal.getOptions().setFixed(portal.getDestinationName().length() > 0 || portal.getOptions().isRandom() || + public static void registerPortal(@NotNull Portal portal) { + portal.getOptions().setFixed(!portal.getDestinationName().isEmpty() || portal.getOptions().isRandom() || portal.getOptions().isBungee()); String portalName = portal.getCleanName(); diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalSignDrawer.java b/src/main/java/net/knarcraft/stargate/portal/PortalSignDrawer.java index 3dabc9e..1df2211 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalSignDrawer.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalSignDrawer.java @@ -3,16 +3,22 @@ package net.knarcraft.stargate.portal; import net.knarcraft.knarlib.property.ColorConversion; import net.knarcraft.knarlib.util.ColorHelper; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.container.SignData; import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.utility.PermissionHelper; +import net.knarcraft.stargate.utility.SignHelper; import net.md_5.bungee.api.ChatColor; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Map; +import java.util.Objects; /** * The portal sign drawer draws the sing of a given portal @@ -32,7 +38,7 @@ public class PortalSignDrawer { * * @param portal

The portal whose sign this portal sign drawer is responsible for drawing

*/ - public PortalSignDrawer(Portal portal) { + public PortalSignDrawer(@NotNull Portal portal) { this.portal = portal; } @@ -43,7 +49,7 @@ public class PortalSignDrawer { * * @param newHighlightColor

The new highlight color

*/ - public static void setHighlightColor(ChatColor newHighlightColor) { + public static void setHighlightColor(@NotNull ChatColor newHighlightColor) { highlightColor = newHighlightColor; } @@ -54,7 +60,7 @@ public class PortalSignDrawer { * * @param newMainColor

The new main sign color

*/ - public static void setMainColor(ChatColor newMainColor) { + public static void setMainColor(@NotNull ChatColor newMainColor) { mainColor = newMainColor; } @@ -63,7 +69,7 @@ public class PortalSignDrawer { * * @param freeColor

The new color to use for marking free stargates

*/ - public static void setFreeColor(ChatColor freeColor) { + public static void setFreeColor(@NotNull ChatColor freeColor) { PortalSignDrawer.freeColor = freeColor; } @@ -72,7 +78,7 @@ public class PortalSignDrawer { * * @param signMainColors

The per-sign main colors

*/ - public static void setPerSignMainColors(Map signMainColors) { + public static void setPerSignMainColors(@NotNull Map signMainColors) { PortalSignDrawer.perSignMainColors = signMainColors; } @@ -81,7 +87,7 @@ public class PortalSignDrawer { * * @param signHighlightColors

The per-sign highlight colors

*/ - public static void setPerSignHighlightColors(Map signHighlightColors) { + public static void setPerSignHighlightColors(@NotNull Map signHighlightColors) { PortalSignDrawer.perSignHighlightColors = signHighlightColors; } @@ -90,6 +96,7 @@ public class PortalSignDrawer { * * @return

The currently used main sign color

*/ + @NotNull public static ChatColor getMainColor() { return mainColor; } @@ -99,6 +106,7 @@ public class PortalSignDrawer { * * @return

The currently used highlighting sign color

*/ + @NotNull public static ChatColor getHighlightColor() { return highlightColor; } @@ -112,8 +120,7 @@ public class PortalSignDrawer { return; } - SignData signData = new SignData(sign, getMainColor(sign.getType()), getHighlightColor(sign.getType())); - drawSign(signData); + drawSign(new SignData(sign, getMainColor(sign.getType()), getHighlightColor(sign.getType()))); } /** @@ -121,6 +128,7 @@ public class PortalSignDrawer { * * @return

The sign of this sign drawer's portal

*/ + @Nullable private Sign getSign() { Block signBlock = portal.getSignLocation().getBlock(); BlockState state = signBlock.getState(); @@ -140,42 +148,63 @@ public class PortalSignDrawer { * * @param signData

All necessary sign information

*/ - private void drawSign(SignData signData) { + private void drawSign(@NotNull SignData signData) { Sign sign = signData.getSign(); ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor mainColor = signData.getMainSignColor(); - //Clear sign - clearSign(sign); + + // Initialize all lines as empty to prevent null + String[] lines = new String[4]; + lines[0] = ""; + lines[1] = ""; + lines[2] = ""; + lines[3] = ""; + setLine(signData, 0, highlightColor + "-" + mainColor + translateAllColorCodes(portal.getName()) + - highlightColor + "-"); + highlightColor + "-", lines); if (!portal.getPortalActivator().isActive()) { //Default sign text - drawInactiveSign(signData); + drawInactiveSign(signData, lines); } else { if (portal.getOptions().isBungee()) { //Bungee sign - drawBungeeSign(signData); + drawBungeeSign(signData, lines); } else if (portal.getOptions().isFixed()) { //Sign pointing at one other portal - drawFixedSign(signData); + drawFixedSign(signData, lines); } else { //Networking stuff - drawNetworkSign(signData); + drawNetworkSign(signData, lines); } } - sign.update(); + updateSign(sign, lines); } /** - * Clears all lines of a sign, but does not update the sign + * Updates a sign, if necessary * - * @param sign

The sign to clear

+ * @param sign

The sign to update

+ * @param lines

The sign's new lines

*/ - private void clearSign(Sign sign) { - for (int index = 0; index <= 3; index++) { - sign.setLine(index, ""); + private void updateSign(@NotNull Sign sign, @NotNull String[] lines) { + boolean updateNecessary = false; + + String[] oldLines = SignHelper.getLines(sign); + for (int i = 0; i < 4; i++) { + if (!oldLines[i].equals(lines[i])) { + updateNecessary = true; + break; + } + } + + if (updateNecessary) { + for (int i = 0; i < 4; i++) { + SignHelper.setSignLine(sign, i, lines[i]); + } + + sign.update(); } } @@ -187,8 +216,12 @@ public class PortalSignDrawer { if (sign == null) { return; } - clearSign(sign); - sign.setLine(0, translateAllColorCodes(portal.getName())); + + for (int index = 0; index <= 3; index++) { + SignHelper.setSignLine(sign, index, ""); + } + + SignHelper.setSignLine(sign, 0, translateAllColorCodes(portal.getName())); sign.update(); } @@ -196,8 +229,9 @@ public class PortalSignDrawer { * Draws a sign with choose-able network locations * * @param signData

All necessary sign information

+ * @param output

The output list to write to

*/ - private void drawNetworkSign(SignData signData) { + private void drawNetworkSign(@NotNull SignData signData, @NotNull String[] output) { PortalActivator destinations = portal.getPortalActivator(); int maxIndex = destinations.getDestinations().size() - 1; int signLineIndex = 0; @@ -207,21 +241,21 @@ public class PortalSignDrawer { //Last, and not only entry. Draw the entry two back if ((destinationIndex == maxIndex) && (maxIndex > 1)) { - drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 2); + drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 2, output); } //Not first entry. Draw the previous entry if (destinationIndex > 0) { - drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 1); + drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex - 1, output); } //Draw the chosen entry (line 2 or 3) - drawNetworkSignChosenLine(signData, freeGatesColored, ++signLineIndex); + drawNetworkSignChosenLine(signData, freeGatesColored, ++signLineIndex, output); //Has another entry and space on the sign if ((maxIndex >= destinationIndex + 1)) { - drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex + 1); + drawNetworkSignLine(signData, freeGatesColored, ++signLineIndex, destinationIndex + 1, output); } //Has another entry and space on the sign if ((maxIndex >= destinationIndex + 2) && (++signLineIndex <= 3)) { - drawNetworkSignLine(signData, freeGatesColored, signLineIndex, destinationIndex + 2); + drawNetworkSignLine(signData, freeGatesColored, signLineIndex, destinationIndex + 2, output); } } @@ -231,19 +265,21 @@ public class PortalSignDrawer { * @param signData

All necessary sign information

* @param freeGatesColored

Whether to display free gates in a different color

* @param signLineIndex

The line to draw on

+ * @param output

The output list to write to

*/ - private void drawNetworkSignChosenLine(SignData signData, boolean freeGatesColored, int signLineIndex) { + private void drawNetworkSignChosenLine(@NotNull SignData signData, boolean freeGatesColored, int signLineIndex, + @NotNull String[] output) { ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor mainColor = signData.getMainSignColor(); if (freeGatesColored) { Portal destination = PortalHandler.getByName(portal.getDestinationName(), portal.getNetwork()); - boolean free = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination); + boolean free = PermissionHelper.isFree(Objects.requireNonNull(portal.getActivePlayer()), portal, destination); ChatColor nameColor = (free ? freeColor : highlightColor); setLine(signData, signLineIndex, nameColor + ">" + (free ? freeColor : mainColor) + - translateAllColorCodes(portal.getDestinationName()) + nameColor + "<"); + translateAllColorCodes(portal.getDestinationName()) + nameColor + "<", output); } else { setLine(signData, signLineIndex, highlightColor + ">" + mainColor + - translateAllColorCodes(portal.getDestinationName()) + highlightColor + "<"); + translateAllColorCodes(portal.getDestinationName()) + highlightColor + "<", output); } } @@ -253,10 +289,11 @@ public class PortalSignDrawer { * @param signData

All necessary sign information

* @param index

The index of the sign line to change

* @param text

The new text on the sign

+ * @param output

The output list to write to

*/ - public void setLine(SignData signData, int index, String text) { + public void setLine(@NotNull SignData signData, int index, @NotNull String text, @NotNull String[] output) { ChatColor mainColor = signData.getMainSignColor(); - signData.getSign().setLine(index, mainColor + text); + output[index] = mainColor + text; } /** @@ -266,17 +303,19 @@ public class PortalSignDrawer { * @param freeGatesColored

Whether to display free gates in a different color

* @param signLineIndex

The line to draw on

* @param destinationIndex

The index of the destination to draw

+ * @param output

The output list to write to

*/ - private void drawNetworkSignLine(SignData signData, boolean freeGatesColored, int signLineIndex, int destinationIndex) { + private void drawNetworkSignLine(@NotNull SignData signData, boolean freeGatesColored, int signLineIndex, + int destinationIndex, @NotNull String[] output) { ChatColor mainColor = signData.getMainSignColor(); PortalActivator destinations = portal.getPortalActivator(); String destinationName = destinations.getDestinations().get(destinationIndex); if (freeGatesColored) { Portal destination = PortalHandler.getByName(destinationName, portal.getNetwork()); - boolean free = PermissionHelper.isFree(portal.getActivePlayer(), portal, destination); - setLine(signData, signLineIndex, (free ? freeColor : mainColor) + translateAllColorCodes(destinationName)); + boolean free = PermissionHelper.isFree(Objects.requireNonNull(portal.getActivePlayer()), portal, destination); + setLine(signData, signLineIndex, (free ? freeColor : mainColor) + translateAllColorCodes(destinationName), output); } else { - setLine(signData, signLineIndex, mainColor + translateAllColorCodes(destinationName)); + setLine(signData, signLineIndex, mainColor + translateAllColorCodes(destinationName), output); } } @@ -284,15 +323,16 @@ public class PortalSignDrawer { * Draws the sign of a BungeeCord portal * * @param signData

All necessary sign information

+ * @param output

The output list to write to

*/ - private void drawBungeeSign(SignData signData) { + private void drawBungeeSign(@NotNull SignData signData, @NotNull String[] output) { ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor mainColor = signData.getMainSignColor(); - setLine(signData, 1, Stargate.getString("bungeeSign")); - setLine(signData, 2, highlightColor + ">" + mainColor + translateAllColorCodes(portal.getDestinationName()) + - highlightColor + "<"); + setLine(signData, 1, new SGFormatBuilder(Message.BUNGEE_SIGN).toString(), output); + setLine(signData, 2, highlightColor + ">" + mainColor + + translateAllColorCodes(portal.getDestinationName()) + highlightColor + "<", output); setLine(signData, 3, highlightColor + "[" + mainColor + translateAllColorCodes(portal.getNetwork()) + - highlightColor + "]"); + highlightColor + "]", output); } /** @@ -301,17 +341,18 @@ public class PortalSignDrawer { *

The sign for an in-active portal should display the right-click prompt and the network.

* * @param signData

All necessary sign information

+ * @param output

The output list to write to

*/ - private void drawInactiveSign(SignData signData) { + private void drawInactiveSign(@NotNull SignData signData, @NotNull String[] output) { ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor mainColor = signData.getMainSignColor(); - setLine(signData, 1, Stargate.getString("signRightClick")); - setLine(signData, 2, Stargate.getString("signToUse")); + setLine(signData, 1, new SGFormatBuilder(Message.SIGN_RIGHT_CLICK).toString(), output); + setLine(signData, 2, new SGFormatBuilder(Message.SIGN_TO_USE).toString(), output); if (!portal.getOptions().isNoNetwork()) { setLine(signData, 3, highlightColor + "(" + mainColor + translateAllColorCodes(portal.getNetwork()) + - highlightColor + ")"); + highlightColor + ")", output); } else { - setLine(signData, 3, ""); + setLine(signData, 3, "", output); } } @@ -319,29 +360,28 @@ public class PortalSignDrawer { * Draws a sign pointing to a fixed location * * @param signData

All necessary sign information

+ * @param output

The output list to write to

*/ - private void drawFixedSign(SignData signData) { + private void drawFixedSign(@NotNull SignData signData, @NotNull String[] output) { ChatColor highlightColor = signData.getHighlightSignColor(); ChatColor mainColor = signData.getMainSignColor(); - Portal destinationPortal = PortalHandler.getByName(Portal.cleanString(portal.getDestinationName()), - portal.getCleanNetwork()); - String destinationName = portal.getOptions().isRandom() ? Stargate.getString("signRandom") : + Portal destinationPortal = PortalHandler.getByName(portal.getDestinationName(), portal.getCleanNetwork()); + String destinationName = portal.getOptions().isRandom() ? new SGFormatBuilder(Message.SIGN_RANDOM).toString() : (destinationPortal != null ? destinationPortal.getName() : portal.getDestinationName()); setLine(signData, 1, highlightColor + ">" + mainColor + translateAllColorCodes(destinationName) + - highlightColor + "<"); + highlightColor + "<", output); if (portal.getOptions().isNoNetwork()) { - setLine(signData, 2, ""); + setLine(signData, 2, "", output); } else { setLine(signData, 2, highlightColor + "(" + mainColor + - translateAllColorCodes(portal.getNetwork()) + highlightColor + ")"); + translateAllColorCodes(portal.getNetwork()) + highlightColor + ")", output); } - Portal destination = PortalHandler.getByName(Portal.cleanString(portal.getDestinationName()), - portal.getNetwork()); + Portal destination = PortalHandler.getByName(portal.getDestinationName(), portal.getNetwork()); if (destination == null && !portal.getOptions().isRandom()) { - setLine(signData, 3, errorColor + Stargate.getString("signDisconnected")); + setLine(signData, 3, errorColor + new SGFormatBuilder(Message.SIGN_DISCONNECTED).toString(), output); } else { - setLine(signData, 3, ""); + setLine(signData, 3, "", output); } } @@ -352,12 +392,13 @@ public class PortalSignDrawer { * @param gateName

The name of the invalid gate type

* @param lineIndex

The index of the line the invalid portal was found at

*/ - public static void markPortalWithInvalidGate(PortalLocation portalLocation, String gateName, int lineIndex) { + public static void markPortalWithInvalidGate(@NotNull PortalLocation portalLocation, @NotNull String gateName, + int lineIndex) { BlockState blockState = portalLocation.getSignLocation().getBlock().getState(); if (!(blockState instanceof Sign sign)) { return; } - sign.setLine(3, errorColor + Stargate.getString("signInvalidGate")); + SignHelper.setSignLine(sign, 3, errorColor + new SGFormatBuilder(Message.SIGN_INVALID).toString()); sign.update(); Stargate.logInfo(String.format("Gate layout on line %d does not exist [%s]", lineIndex, gateName)); @@ -369,7 +410,8 @@ public class PortalSignDrawer { * @param signType

The sign type to get the main color for

* @return

The main color for the given sign type

*/ - private ChatColor getMainColor(Material signType) { + @NotNull + private ChatColor getMainColor(@NotNull Material signType) { ChatColor signColor = perSignMainColors.get(signType); if (signColor == null) { return mainColor; @@ -384,7 +426,8 @@ public class PortalSignDrawer { * @param signType

The sign type to get the highlight color for

* @return

The highlight color for the given sign type

*/ - private ChatColor getHighlightColor(Material signType) { + @NotNull + private ChatColor getHighlightColor(@NotNull Material signType) { ChatColor signColor = perSignHighlightColors.get(signType); if (signColor == null) { return highlightColor; @@ -399,7 +442,8 @@ public class PortalSignDrawer { * @param input

The input to translate color codes for

* @return

The input with color codes converted translated from & to §

*/ - private String translateAllColorCodes(String input) { + @NotNull + private String translateAllColorCodes(@NotNull String input) { return ColorHelper.translateColorCodes(input, ColorConversion.RGB); } diff --git a/src/main/java/net/knarcraft/stargate/portal/property/PortalLocation.java b/src/main/java/net/knarcraft/stargate/portal/property/PortalLocation.java index 49c09ff..10fa189 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/PortalLocation.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/PortalLocation.java @@ -5,6 +5,8 @@ import net.knarcraft.stargate.container.RelativeBlockVector; import org.bukkit.Axis; import org.bukkit.World; import org.bukkit.block.BlockFace; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Keeps track of location related data for a portal @@ -23,6 +25,7 @@ public class PortalLocation { * * @return

The top-left block of the portal

*/ + @NotNull public BlockLocation getTopLeft() { return topLeft; } @@ -41,6 +44,7 @@ public class PortalLocation { * * @return

The location of the portal's sign

*/ + @NotNull public BlockLocation getSignLocation() { return signLocation; } @@ -50,6 +54,7 @@ public class PortalLocation { * * @return

The relative location of the portal's button

*/ + @Nullable public RelativeBlockVector getButtonVector() { return buttonVector; } @@ -59,16 +64,19 @@ public class PortalLocation { * * @return

The button's block face

*/ + @NotNull public BlockFace getButtonFacing() { return buttonFacing; } /** * Gets the rotation axis, which is the axis along which the gate is placed + * *

The portal's rotation axis is the cross axis of the button's axis

* * @return

The portal's rotation axis

*/ + @NotNull public Axis getRotationAxis() { return getYaw() == 0.0F || getYaw() == 180.0F ? Axis.X : Axis.Z; } @@ -78,6 +86,7 @@ public class PortalLocation { * * @return

The world this portal resides in

*/ + @Nullable public World getWorld() { return topLeft.getWorld(); } @@ -91,7 +100,8 @@ public class PortalLocation { * @param topLeft

The new top-left block of the portal's square structure

* @return

The portal location Object

*/ - public PortalLocation setTopLeft(BlockLocation topLeft) { + @NotNull + public PortalLocation setTopLeft(@NotNull BlockLocation topLeft) { this.topLeft = topLeft; return this; } @@ -104,6 +114,7 @@ public class PortalLocation { * @param yaw

The portal's new yaw

* @return

The portal location Object

*/ + @NotNull public PortalLocation setYaw(float yaw) { this.yaw = yaw; return this; @@ -115,7 +126,8 @@ public class PortalLocation { * @param signLocation

The new sign location

* @return

The portal location Object

*/ - public PortalLocation setSignLocation(BlockLocation signLocation) { + @NotNull + public PortalLocation setSignLocation(@NotNull BlockLocation signLocation) { this.signLocation = signLocation; return this; } @@ -126,7 +138,8 @@ public class PortalLocation { * @param buttonVector

The new relative button location

* @return

The portal location Object

*/ - public PortalLocation setButtonVector(RelativeBlockVector buttonVector) { + @NotNull + public PortalLocation setButtonVector(@Nullable RelativeBlockVector buttonVector) { this.buttonVector = buttonVector; return this; } @@ -137,7 +150,8 @@ public class PortalLocation { * @param buttonFacing

The new block face of the portal's button

* @return

The portal location Object

*/ - public PortalLocation setButtonFacing(BlockFace buttonFacing) { + @NotNull + public PortalLocation setButtonFacing(@NotNull BlockFace buttonFacing) { this.buttonFacing = buttonFacing; return this; } diff --git a/src/main/java/net/knarcraft/stargate/portal/property/PortalOption.java b/src/main/java/net/knarcraft/stargate/portal/property/PortalOption.java index 9b4cce3..c3fe5cf 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/PortalOption.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/PortalOption.java @@ -1,5 +1,7 @@ package net.knarcraft.stargate.portal.property; +import org.jetbrains.annotations.NotNull; + /** * Each enum value represents one option a portal can have/use */ @@ -8,57 +10,57 @@ public enum PortalOption { /** * This option allows a portal to be hidden from others */ - HIDDEN('h', "stargate.option.hidden", 11), + HIDDEN('h', "hidden", 11), /** * This option allows a portal that's always on and does not need to be activated or opened each time */ - ALWAYS_ON('a', "stargate.option.alwayson", 12), + ALWAYS_ON('a', "alwayson", 12), /** * This option allows a portal that's private to the stargate's owner */ - PRIVATE('p', "stargate.option.private", 13), + PRIVATE('p', "private", 13), /** * This option allows a portal that's free even if stargates usually are not */ - FREE('f', "stargate.option.free", 15), + FREE('f', "free", 15), /** * This option allows a portal where players exit through the back of the portal */ - BACKWARDS('b', "stargate.option.backwards", 16), + BACKWARDS('b', "backwards", 16), /** * This option shows the gate in the network list even if it's always on */ - SHOW('s', "stargate.option.show", 17), + SHOW('s', "show", 17), /** * This option hides the network name on the sign */ - NO_NETWORK('n', "stargate.option.nonetwork", 18), + NO_NETWORK('n', "nonetwork", 18), /** * This option allows a portal where players teleport to a random exit portal in the network */ - RANDOM('r', "stargate.option.random", 19), + RANDOM('r', "random", 19), /** * This option allows a portal to teleport to another server connected through BungeeCord */ - BUNGEE('u', "stargate.admin.bungee", 20), + BUNGEE('u', "bungee", 20), /** * This option allows a portal which does not display a teleportation message, for better immersion */ - SILENT('i', "stargate.option.silent", 21), + QUIET('q', "quiet", 21), /** * This option causes a fixed portal's sign to be removed after creation */ - NO_SIGN('e', "stargate.option.nosign", 22); + NO_SIGN('v', "nosign", 22); private final char characterRepresentation; private final String permissionString; @@ -70,9 +72,9 @@ public enum PortalOption { * @param characterRepresentation

The character representation used on the sign to allow this option

* @param permissionString

The permission necessary to use this option

*/ - PortalOption(final char characterRepresentation, String permissionString, int saveIndex) { + PortalOption(final char characterRepresentation, @NotNull String permissionString, int saveIndex) { this.characterRepresentation = characterRepresentation; - this.permissionString = permissionString; + this.permissionString = "stargate.option." + permissionString; this.saveIndex = saveIndex; } @@ -90,6 +92,7 @@ public enum PortalOption { * * @return

The permission necessary for this option

*/ + @NotNull public String getPermissionString() { return this.permissionString; } diff --git a/src/main/java/net/knarcraft/stargate/portal/property/PortalOptions.java b/src/main/java/net/knarcraft/stargate/portal/property/PortalOptions.java index 03cf40f..570b74d 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/PortalOptions.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/PortalOptions.java @@ -1,6 +1,7 @@ package net.knarcraft.stargate.portal.property; import net.knarcraft.stargate.Stargate; +import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -18,7 +19,7 @@ public class PortalOptions { * @param options

All options to keep track of

* @param hasDestination

Whether the portal has a fixed destination

*/ - public PortalOptions(Map options, boolean hasDestination) { + public PortalOptions(@NotNull Map options, boolean hasDestination) { this.options = options; isFixed = hasDestination || this.isRandom() || this.isBungee(); @@ -171,15 +172,15 @@ public class PortalOptions { } /** - * Gets whether this portal is silent + * Gets whether this portal is QUIET * - *

A silent portal does not output anything to the chat when teleporting. This option is mainly useful to keep + *

A quiet portal does not output anything to the chat when teleporting. This option is mainly useful to keep * the immersion during teleportation (for role-playing servers or similar).

* - * @return

Whether this portal is silent

+ * @return

Whether this portal is quiet

*/ - public boolean isSilent() { - return this.options.get(PortalOption.SILENT); + public boolean isQuiet() { + return this.options.get(PortalOption.QUIET); } /** diff --git a/src/main/java/net/knarcraft/stargate/portal/property/PortalOwner.java b/src/main/java/net/knarcraft/stargate/portal/property/PortalOwner.java index 307621e..9bb9389 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/PortalOwner.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/PortalOwner.java @@ -3,7 +3,8 @@ package net.knarcraft.stargate.portal.property; import net.knarcraft.stargate.Stargate; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.UUID; @@ -20,7 +21,7 @@ public class PortalOwner { * * @param ownerIdentifier

A UUID, or a username for legacy support

*/ - public PortalOwner(String ownerIdentifier) { + public PortalOwner(@NotNull String ownerIdentifier) { parseIdentifier(ownerIdentifier); } @@ -29,7 +30,7 @@ public class PortalOwner { * * @param player

The player which is the owner of the portal

*/ - public PortalOwner(Player player) { + public PortalOwner(@NotNull OfflinePlayer player) { this.ownerUUID = player.getUniqueId(); this.ownerName = player.getName(); } @@ -39,6 +40,7 @@ public class PortalOwner { * * @return

The UUID of this owner, or null if a UUID is not available

*/ + @Nullable public UUID getUUID() { return ownerUUID; } @@ -51,7 +53,7 @@ public class PortalOwner { * * @param uniqueId

The new unique id for the portal owner

*/ - public void setUUID(UUID uniqueId) { + public void setUUID(@NotNull UUID uniqueId) { if (ownerUUID == null) { ownerUUID = uniqueId; } else { @@ -64,7 +66,14 @@ public class PortalOwner { * * @return

The name of this owner

*/ + @NotNull public String getName() { + if (this.ownerUUID != null && this.ownerName == null) { + this.ownerName = fetchName(); + if (this.ownerName == null) { + this.ownerName = "UNKNOWN!"; + } + } return ownerName; } @@ -76,6 +85,7 @@ public class PortalOwner { * * @return

The owner's identifier

*/ + @NotNull public String getIdentifier() { if (ownerUUID != null) { return ownerUUID.toString(); @@ -93,16 +103,14 @@ public class PortalOwner { * * @param ownerIdentifier

The identifier for a portal's owner

*/ - private void parseIdentifier(String ownerIdentifier) { + private void parseIdentifier(@NotNull String ownerIdentifier) { UUID ownerUUID = null; - String ownerName; + String ownerName = null; if (ownerIdentifier.length() > 16) { //If more than 16 characters, the string cannot be a username, so it's probably a UUID try { ownerUUID = UUID.fromString(ownerIdentifier); - OfflinePlayer offlineOwner = Bukkit.getServer().getOfflinePlayer(ownerUUID); - ownerName = offlineOwner.getName(); - } catch (IllegalArgumentException ex) { + } catch (IllegalArgumentException exception) { //Invalid as UUID and username, so just keep it as owner name and hope the server owner fixes it ownerName = ownerIdentifier; Stargate.debug("loadAllPortals", "Invalid stargate owner string: " + ownerIdentifier); @@ -115,4 +123,14 @@ public class PortalOwner { this.ownerUUID = ownerUUID; } + /** + * Gets the name of a player + * + * @return

The player to get the name of

+ */ + @Nullable + private String fetchName() { + return Bukkit.getServer().getOfflinePlayer(ownerUUID).getName(); + } + } diff --git a/src/main/java/net/knarcraft/stargate/portal/property/PortalStrings.java b/src/main/java/net/knarcraft/stargate/portal/property/PortalStrings.java new file mode 100644 index 0000000..839a6e9 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/portal/property/PortalStrings.java @@ -0,0 +1,13 @@ +package net.knarcraft.stargate.portal.property; + +import org.jetbrains.annotations.NotNull; + +/** + * A record of a portal's string values + * + * @param name

The name of the portal

+ * @param network

The name of the network the portal belongs to

+ * @param destination

The name of the portal's destination

+ */ +public record PortalStrings(@NotNull String name, @NotNull String network, @NotNull String destination) { +} diff --git a/src/main/java/net/knarcraft/stargate/portal/property/PortalStructure.java b/src/main/java/net/knarcraft/stargate/portal/property/PortalStructure.java index c5c2986..4308888 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/PortalStructure.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/PortalStructure.java @@ -5,6 +5,8 @@ import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.property.gate.Gate; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * The portal structure is responsible for the physical properties of a portal @@ -19,7 +21,6 @@ public class PortalStructure { private BlockLocation button; private BlockLocation[] frame; private BlockLocation[] entrances; - private boolean verified; /** * Instantiates a new portal structure @@ -28,10 +29,9 @@ public class PortalStructure { * @param gate

The gate type used by this portal structure

* @param button

The real location of the portal's button

*/ - public PortalStructure(Portal portal, Gate gate, BlockLocation button) { + public PortalStructure(@NotNull Portal portal, @NotNull Gate gate, @Nullable BlockLocation button) { this.portal = portal; this.gate = gate; - this.verified = false; this.button = button; } @@ -40,6 +40,7 @@ public class PortalStructure { * * @return

The gate used by this portal structure

*/ + @NotNull public Gate getGate() { return gate; } @@ -49,6 +50,7 @@ public class PortalStructure { * * @return

The location of this portal's button

*/ + @Nullable public BlockLocation getButton() { return button; } @@ -58,39 +60,10 @@ public class PortalStructure { * * @param button

The location of this portal's button

*/ - public void setButton(BlockLocation button) { + public void setButton(@NotNull BlockLocation button) { this.button = button; } - /** - * Verifies that all control blocks in this portal follows its gate template - * - * @return

True if all control blocks were verified

- */ - public boolean isVerified() { - boolean verified = true; - if (!Stargate.getGateConfig().verifyPortals()) { - return true; - } - for (RelativeBlockVector control : gate.getLayout().getControls()) { - verified = verified && portal.getBlockAt(control).getBlock().getType().equals(gate.getControlBlock()); - } - this.verified = verified; - return verified; - } - - /** - * Gets the result of the last portal verification - * - * @return

True if this portal was verified

- */ - public boolean wasVerified() { - if (!Stargate.getGateConfig().verifyPortals()) { - return true; - } - return verified; - } - /** * Checks if all blocks in a gate matches the gate template * @@ -113,7 +86,8 @@ public class PortalStructure { * @param vectors

The relative block vectors to convert

* @return

A list of block locations

*/ - private BlockLocation[] relativeBlockVectorsToBlockLocations(RelativeBlockVector[] vectors) { + @NotNull + private BlockLocation[] relativeBlockVectorsToBlockLocations(@NotNull RelativeBlockVector[] vectors) { BlockLocation[] locations = new BlockLocation[vectors.length]; for (int i = 0; i < vectors.length; i++) { locations[i] = portal.getBlockAt(vectors[i]); @@ -126,6 +100,7 @@ public class PortalStructure { * * @return

The locations of this portal's entrances

*/ + @NotNull public BlockLocation[] getEntrances() { if (entrances == null) { //Get the locations of the entrances once, and only if necessary as it's an expensive operation @@ -139,6 +114,7 @@ public class PortalStructure { * * @return

The locations of this portal's frame

*/ + @NotNull public BlockLocation[] getFrame() { if (frame == null) { //Get the locations of the frame blocks once, and only if necessary as it's an expensive operation diff --git a/src/main/java/net/knarcraft/stargate/portal/property/gate/Gate.java b/src/main/java/net/knarcraft/stargate/portal/property/gate/Gate.java index bfde5e4..7fe235c 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/gate/Gate.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/gate/Gate.java @@ -1,14 +1,21 @@ package net.knarcraft.stargate.portal.property.gate; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier; +import net.knarcraft.stargate.config.material.MaterialSpecifier; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.RelativeBlockVector; +import net.knarcraft.stargate.utility.MaterialHelper; import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.BufferedWriter; +import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -20,11 +27,13 @@ public class Gate { private final String filename; private final GateLayout layout; - private final Map characterMaterialMap; + private final Map> characterMaterialMap; + //Gate materials - private final Material portalOpenBlock; - private final Material portalClosedBlock; - private final Material portalButton; + private final List portalOpenMaterials; + private final List portalClosedMaterials; + private final List portalButtonMaterials; + //Economy information private final int useCost; private final int createCost; @@ -34,30 +43,29 @@ public class Gate { /** * Instantiates a new gate * - * @param filename

The name of the gate file, including extension

- * @param layout

The gate layout defined in the gate file

- * @param characterMaterialMap

The material types the different layout characters represent

- * @param portalOpenBlock

The material to set the opening to when the portal is open

- * @param portalClosedBlock

The material to set the opening to when the portal is closed

- * @param portalButton

The material to use for the portal button

- * @param useCost

The cost of using a portal with this gate layout (-1 to disable)

- * @param createCost

The cost of creating a portal with this gate layout (-1 to disable)

- * @param destroyCost

The cost of destroying a portal with this gate layout (-1 to disable)

- * @param toOwner

Whether any payment should go to the owner of the gate, as opposed to just disappearing

+ * @param filename

The name of the gate file, including extension

+ * @param layout

The gate layout defined in the gate file

+ * @param characterMaterialsMap

The material types the different layout characters represent

+ * @param portalOpenMaterials

The material to set the opening to when the portal is open

+ * @param portalClosedMaterials

The material to set the opening to when the portal is closed

+ * @param portalButtonMaterials

The material to use for the portal button

+ * @param gateCosts

The costs and other economy information for the gate

*/ - public Gate(String filename, GateLayout layout, Map characterMaterialMap, Material portalOpenBlock, - Material portalClosedBlock, Material portalButton, int useCost, int createCost, int destroyCost, - boolean toOwner) { + public Gate(@NotNull String filename, @NotNull GateLayout layout, + @NotNull Map> characterMaterialsMap, + @NotNull List portalOpenMaterials, + @NotNull List portalClosedMaterials, + @NotNull List portalButtonMaterials, @NotNull GateCosts gateCosts) { this.filename = filename; this.layout = layout; - this.characterMaterialMap = characterMaterialMap; - this.portalOpenBlock = portalOpenBlock; - this.portalClosedBlock = portalClosedBlock; - this.portalButton = portalButton; - this.useCost = useCost; - this.createCost = createCost; - this.destroyCost = destroyCost; - this.toOwner = toOwner; + this.characterMaterialMap = characterMaterialsMap; + this.portalOpenMaterials = portalOpenMaterials; + this.portalClosedMaterials = portalClosedMaterials; + this.portalButtonMaterials = portalButtonMaterials; + this.useCost = gateCosts.useCost(); + this.createCost = gateCosts.createCost(); + this.destroyCost = gateCosts.destroyCost(); + this.toOwner = gateCosts.toOwner(); } /** @@ -65,6 +73,7 @@ public class Gate { * * @return

This gate's layout

*/ + @NotNull public GateLayout getLayout() { return layout; } @@ -74,7 +83,8 @@ public class Gate { * * @return

The character to material map

*/ - public Map getCharacterMaterialMap() { + @NotNull + public Map> getCharacterMaterialMap() { return new HashMap<>(characterMaterialMap); } @@ -83,7 +93,8 @@ public class Gate { * * @return

The material type used for control blocks

*/ - public Material getControlBlock() { + @NotNull + public List getControlBlockMaterials() { return characterMaterialMap.get(GateHandler.getControlBlockCharacter()); } @@ -92,6 +103,7 @@ public class Gate { * * @return

The filename of this gate's file

*/ + @NotNull public String getFilename() { return filename; } @@ -101,8 +113,9 @@ public class Gate { * * @return

The block type to use for the opening when open

*/ - public Material getPortalOpenBlock() { - return portalOpenBlock; + @NotNull + public List getPortalOpenMaterials() { + return portalOpenMaterials; } /** @@ -110,8 +123,9 @@ public class Gate { * * @return

The block type to use for the opening when closed

*/ - public Material getPortalClosedBlock() { - return portalClosedBlock; + @NotNull + public List getPortalClosedMaterials() { + return portalClosedMaterials; } /** @@ -119,8 +133,9 @@ public class Gate { * * @return

The material to use for a portal's button if using this gate type

*/ - public Material getPortalButton() { - return portalButton; + @NotNull + public List getPortalButtonMaterials() { + return portalButtonMaterials; } /** @@ -137,6 +152,7 @@ public class Gate { * * @return

The cost of creating a portal with this gate

*/ + @NotNull public Integer getCreateCost() { return createCost < 0 ? Stargate.getEconomyConfig().getDefaultCreateCost() : createCost; } @@ -146,6 +162,7 @@ public class Gate { * * @return

The cost of destroying a portal with this gate

*/ + @NotNull public Integer getDestroyCost() { return destroyCost < 0 ? Stargate.getEconomyConfig().getDefaultDestroyCost() : destroyCost; } @@ -155,6 +172,7 @@ public class Gate { * * @return

Whether portal payments go to the owner

*/ + @NotNull public Boolean getToOwner() { return toOwner; } @@ -166,7 +184,7 @@ public class Gate { * @param yaw

The yaw when looking directly outwards

* @return

True if this gate matches the portal

*/ - public boolean matches(BlockLocation topLeft, double yaw) { + public boolean matches(@NotNull BlockLocation topLeft, double yaw) { return matches(topLeft, yaw, false); } @@ -182,7 +200,7 @@ public class Gate { * @param onCreate

Whether this is used in the context of creating a new gate

* @return

True if this gate matches the portal

*/ - public boolean matches(BlockLocation topLeft, double yaw, boolean onCreate) { + public boolean matches(@NotNull BlockLocation topLeft, double yaw, boolean onCreate) { return verifyGateEntrancesMatch(topLeft, yaw, onCreate) && verifyGateBorderMatches(topLeft, yaw); } @@ -193,28 +211,29 @@ public class Gate { * @param yaw

The yaw when looking directly outwards from the portal

* @return

True if all border blocks of the gate match the layout

*/ - private boolean verifyGateBorderMatches(BlockLocation topLeft, double yaw) { - Map characterMaterialMap = new HashMap<>(this.characterMaterialMap); + private boolean verifyGateBorderMatches(@NotNull BlockLocation topLeft, double yaw) { for (RelativeBlockVector borderVector : layout.getBorder()) { - int rowIndex = borderVector.getRight(); - int lineIndex = borderVector.getDown(); + int rowIndex = borderVector.right(); + int lineIndex = borderVector.down(); Character key = layout.getLayout()[lineIndex][rowIndex]; - Material materialInLayout = characterMaterialMap.get(key); + List materialInLayout = characterMaterialMap.get(key); Material materialAtLocation = topLeft.getRelativeLocation(borderVector, yaw).getType(); - if (materialInLayout == null) { + if (materialInLayout != null) { + if (!MaterialHelper.specifiersToMaterials(materialInLayout).contains(materialAtLocation)) { + Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s", + materialAtLocation, MaterialHelper.specifiersToMaterials(materialInLayout))); + return false; + } + } else { /* This generally should not happen with proper checking, but just in case a material character is not * recognized, but still allowed in previous checks, verify the gate as long as all such instances of * the character correspond to the same material in the physical gate. All subsequent gates will also * need to match the first verified gate. */ - characterMaterialMap.put(key, materialAtLocation); + this.characterMaterialMap.put(key, List.of(new BukkitMaterialSpecifier(materialAtLocation))); Stargate.debug("Gate::Matches", String.format("Missing layout material in %s. Using %s from the" + " physical portal.", getFilename(), materialAtLocation)); - } else if (materialAtLocation != materialInLayout) { - Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s", - materialAtLocation, materialInLayout)); - return false; } } return true; @@ -228,7 +247,7 @@ public class Gate { * @param onCreate

Whether this is used in the context of creating a new gate

* @return

Whether this is used in the context of creating a new gate

*/ - private boolean verifyGateEntrancesMatch(BlockLocation topLeft, double yaw, boolean onCreate) { + private boolean verifyGateEntrancesMatch(@NotNull BlockLocation topLeft, double yaw, boolean onCreate) { Stargate.debug("verifyGateEntrancesMatch", String.valueOf(topLeft)); for (RelativeBlockVector entranceVector : layout.getEntrances()) { Stargate.debug("verifyGateEntrancesMatch", String.valueOf(entranceVector)); @@ -239,7 +258,8 @@ public class Gate { continue; } - if (type != portalClosedBlock && type != portalOpenBlock) { + if (!MaterialHelper.specifiersToMaterials(portalClosedMaterials).contains(type) && + !MaterialHelper.specifiersToMaterials(portalOpenMaterials).contains(type)) { Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type); return false; } @@ -254,20 +274,20 @@ public class Gate { * * @param gateFolder

The folder to save the gate file in

*/ - public void save(String gateFolder) { + public void save(@NotNull String gateFolder) { try { - BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(gateFolder + filename)); + BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File(gateFolder, filename))); //Save main material names - writeConfig(bufferedWriter, "portal-open", portalOpenBlock.name()); - writeConfig(bufferedWriter, "portal-closed", portalClosedBlock.name()); - writeConfig(bufferedWriter, "button", portalButton.name()); + writeConfig(bufferedWriter, "portal-open", MaterialHelper.specifiersToString(portalOpenMaterials)); + writeConfig(bufferedWriter, "portal-closed", MaterialHelper.specifiersToString(portalClosedMaterials)); + writeConfig(bufferedWriter, "button", MaterialHelper.specifiersToString(portalButtonMaterials)); //Save the values necessary for economy saveEconomyValues(bufferedWriter); //Store material types to use for frame blocks - saveFrameBlockTypes(bufferedWriter); + saveFrameBlockType(bufferedWriter); bufferedWriter.newLine(); @@ -275,8 +295,8 @@ public class Gate { layout.saveLayout(bufferedWriter); bufferedWriter.close(); - } catch (IOException ex) { - Stargate.logSevere(String.format("Could not save Gate %s - %s", filename, ex.getMessage())); + } catch (IOException exception) { + Stargate.logSevere(String.format("Could not save Gate %s - %s", filename, exception.getMessage())); } } @@ -286,7 +306,7 @@ public class Gate { * @param bufferedWriter

The buffered writer to write to

* @throws IOException

If unable to write to the buffered writer

*/ - private void saveEconomyValues(BufferedWriter bufferedWriter) throws IOException { + private void saveEconomyValues(@NotNull BufferedWriter bufferedWriter) throws IOException { //Write use cost if not disabled if (useCost != -1) { writeConfig(bufferedWriter, "usecost", useCost); @@ -308,26 +328,37 @@ public class Gate { * @param bufferedWriter

The buffered writer to write to

* @throws IOException

If unable to write to the buffered writer

*/ - private void saveFrameBlockTypes(BufferedWriter bufferedWriter) throws IOException { - for (Map.Entry entry : characterMaterialMap.entrySet()) { - Character type = entry.getKey(); - Material value = entry.getValue(); + private void saveFrameBlockType(@NotNull BufferedWriter bufferedWriter) throws IOException { + for (Map.Entry> entry : this.characterMaterialMap.entrySet()) { + Character key = entry.getKey(); //Skip characters not part of the frame - if (type.equals(GateHandler.getAnythingCharacter()) || - type.equals(GateHandler.getEntranceCharacter()) || - type.equals(GateHandler.getExitCharacter())) { + if (key.equals(GateHandler.getAnythingCharacter()) || + key.equals(GateHandler.getEntranceCharacter()) || + key.equals(GateHandler.getExitCharacter())) { continue; } - - bufferedWriter.append(type); - bufferedWriter.append('='); - if (value != null) { - bufferedWriter.append(value.toString()); - } - bufferedWriter.newLine(); + saveFrameBlockType(key, MaterialHelper.specifiersToString(entry.getValue()), bufferedWriter); } } + /** + * Saves a type of block used for the gate frame/border using a buffered writer + * + * @param key

The character key to store

+ * @param value

The string value to store

+ * @param bufferedWriter

The buffered writer to write to

+ * @throws IOException

If unable to write to the buffered writer

+ */ + private void saveFrameBlockType(@NotNull Character key, @Nullable String value, + @NotNull BufferedWriter bufferedWriter) throws IOException { + bufferedWriter.append(key.toString()); + bufferedWriter.append('='); + if (value != null) { + bufferedWriter.append(value); + } + bufferedWriter.newLine(); + } + /** * Writes a formatted string to a buffered writer * @@ -336,7 +367,8 @@ public class Gate { * @param value

The config value to save

* @throws IOException

If unable to write to the buffered writer

*/ - private void writeConfig(BufferedWriter bufferedWriter, String key, Object value) throws IOException { + private void writeConfig(@NotNull BufferedWriter bufferedWriter, @NotNull String key, + @NotNull Object value) throws IOException { //Figure out the correct formatting to use String format = "%s="; if (value instanceof Boolean) { diff --git a/src/main/java/net/knarcraft/stargate/portal/property/gate/GateCosts.java b/src/main/java/net/knarcraft/stargate/portal/property/gate/GateCosts.java new file mode 100644 index 0000000..93a224d --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/portal/property/gate/GateCosts.java @@ -0,0 +1,12 @@ +package net.knarcraft.stargate.portal.property.gate; + +/** + * The costs assigned to a gate + * + * @param useCost

The cost for using (entering) the gate

+ * @param createCost

The cost for creating a portal with the gate type

+ * @param destroyCost

The cost for destroying a portal with the gate type

+ * @param toOwner

Whether the use cost is paid to the gate's owner

+ */ +public record GateCosts(int useCost, int createCost, int destroyCost, boolean toOwner) { +} diff --git a/src/main/java/net/knarcraft/stargate/portal/property/gate/GateHandler.java b/src/main/java/net/knarcraft/stargate/portal/property/gate/GateHandler.java index 9558175..110a9ab 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/gate/GateHandler.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/gate/GateHandler.java @@ -1,20 +1,26 @@ package net.knarcraft.stargate.portal.property.gate; import net.knarcraft.stargate.Stargate; -import net.knarcraft.stargate.utility.GateReader; +import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier; +import net.knarcraft.stargate.config.material.MaterialSpecifier; import net.knarcraft.stargate.utility.MaterialHelper; +import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Tag; import org.bukkit.block.Block; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; +import java.util.function.Predicate; import static net.knarcraft.stargate.utility.GateReader.generateLayoutMatrix; import static net.knarcraft.stargate.utility.GateReader.readGateConfig; @@ -34,8 +40,9 @@ public class GateHandler { private static final Material defaultPortalBlockClosed = Material.AIR; private static final Material defaultButton = Material.STONE_BUTTON; - private static final HashMap gates = new HashMap<>(); - private static final HashMap> controlBlocks = new HashMap<>(); + private static final Map gates = new HashMap<>(); + private static final Map> controlBlocks = new HashMap<>(); + private static final Map> controlBlockTags = new HashMap<>(); private GateHandler() { @@ -46,6 +53,7 @@ public class GateHandler { * * @return

The character used for blocks that are not part of the gate

*/ + @NotNull public static Character getAnythingCharacter() { return ANYTHING; } @@ -55,6 +63,7 @@ public class GateHandler { * * @return

The character used for defining the entrance

*/ + @NotNull public static Character getEntranceCharacter() { return ENTRANCE; } @@ -64,6 +73,7 @@ public class GateHandler { * * @return

The character used for defining the exit

*/ + @NotNull public static Character getExitCharacter() { return EXIT; } @@ -74,6 +84,7 @@ public class GateHandler { * * @return

The character used for defining control blocks

*/ + @NotNull public static Character getControlBlockCharacter() { return CONTROL_BLOCK; } @@ -83,16 +94,16 @@ public class GateHandler { * * @param gate

The gate to register

*/ - private static void registerGate(Gate gate) { + private static void registerGate(@NotNull Gate gate) { gates.put(gate.getFilename(), gate); - Material blockID = gate.getControlBlock(); - - if (!controlBlocks.containsKey(blockID)) { - controlBlocks.put(blockID, new ArrayList<>()); + Set blockTypes = MaterialHelper.specifiersToMaterials(gate.getControlBlockMaterials()); + for (Material material : blockTypes) { + if (!controlBlocks.containsKey(material)) { + controlBlocks.put(material, new ArrayList<>()); + } + controlBlocks.get(material).add(gate); } - - controlBlocks.get(blockID).add(gate); } /** @@ -101,7 +112,8 @@ public class GateHandler { * @param file

The file containing the gate data

* @return

The loaded gate, or null if unable to load the gate

*/ - private static Gate loadGate(File file) { + @Nullable + private static Gate loadGate(@NotNull File file) { try (Scanner scanner = new Scanner(file)) { return loadGate(file.getName(), file.getParent(), scanner); } catch (Exception exception) { @@ -118,19 +130,20 @@ public class GateHandler { * @param scanner

The scanner to use for reading the gate data

* @return

The loaded gate or null if unable to load the gate

*/ - private static Gate loadGate(String fileName, String parentFolder, Scanner scanner) { + @Nullable + private static Gate loadGate(@NotNull String fileName, @NotNull String parentFolder, + @NotNull Scanner scanner) { List> design = new ArrayList<>(); - Map characterMaterialMap = new HashMap<>(); + Map> characterMaterialMap = new HashMap<>(); Map config = new HashMap<>(); - Set frameTypes = new HashSet<>(); //Initialize character to material map - characterMaterialMap.put(ENTRANCE, Material.AIR); - characterMaterialMap.put(EXIT, Material.AIR); - characterMaterialMap.put(ANYTHING, Material.AIR); + characterMaterialMap.put(ENTRANCE, List.of(new BukkitMaterialSpecifier(Material.AIR))); + characterMaterialMap.put(EXIT, List.of(new BukkitMaterialSpecifier(Material.AIR))); + characterMaterialMap.put(ANYTHING, List.of(new BukkitMaterialSpecifier(Material.AIR))); //Read the file into appropriate lists and maps - int columns = readGateFile(scanner, characterMaterialMap, fileName, design, frameTypes, config); + int columns = readGateFile(scanner, characterMaterialMap, fileName, design, config); if (columns < 0) { return null; } @@ -143,7 +156,7 @@ public class GateHandler { } //Update gate file in case the format has changed between versions - gate.save(parentFolder + "/"); + gate.save(parentFolder); return gate; } @@ -156,23 +169,26 @@ public class GateHandler { * @param characterMaterialMap

A map between layout characters and the material to use

* @return

A new gate, or null if the config is invalid

*/ - private static Gate createGate(Map config, String fileName, Character[][] layout, - Map characterMaterialMap) { + @Nullable + private static Gate createGate(@NotNull Map config, @NotNull String fileName, + @NotNull Character[][] layout, + @NotNull Map> characterMaterialMap) { //Read relevant material types - Material portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen); - Material portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed); - Material portalButton = readGateConfig(config, fileName, "button", defaultButton); + List portalOpenBlock = readGateConfig(config, fileName, "portal-open", defaultPortalBlockOpen); + List portalClosedBlock = readGateConfig(config, fileName, "portal-closed", defaultPortalBlockClosed); + List portalButton = readGateConfig(config, fileName, "button", defaultButton); //Read economy values - int useCost = GateReader.readGateConfig(config, fileName, "usecost"); - int createCost = GateReader.readGateConfig(config, fileName, "createcost"); - int destroyCost = GateReader.readGateConfig(config, fileName, "destroycost"); + int useCost = readGateConfig(config, fileName, "usecost"); + int createCost = readGateConfig(config, fileName, "createcost"); + int destroyCost = readGateConfig(config, fileName, "destroycost"); boolean toOwner = (config.containsKey("toowner") ? Boolean.parseBoolean(config.get("toowner")) : Stargate.getEconomyConfig().sendPaymentToOwner()); + GateCosts gateCosts = new GateCosts(useCost, createCost, destroyCost, toOwner); //Create the new gate Gate gate = new Gate(fileName, new GateLayout(layout), characterMaterialMap, portalOpenBlock, portalClosedBlock, - portalButton, useCost, createCost, destroyCost, toOwner); + portalButton, gateCosts); if (!validateGate(gate, fileName)) { return null; @@ -187,7 +203,7 @@ public class GateHandler { * @param fileName

The filename of the loaded gate file

* @return

True if the gate is valid. False otherwise

*/ - private static boolean validateGate(Gate gate, String fileName) { + private static boolean validateGate(@NotNull Gate gate, @NotNull String fileName) { String failString = String.format("Could not load Gate %s", fileName) + " - %s"; if (gate.getLayout().getControls().length != 2) { @@ -195,23 +211,28 @@ public class GateHandler { return false; } - if (!MaterialHelper.isButtonCompatible(gate.getPortalButton())) { + if (gate.getLayout().getExit() == null) { + Stargate.logSevere(String.format(failString, "Gates must have one specified exit point")); + return false; + } + + if (checkMaterialPredicateFail(gate.getPortalButtonMaterials(), MaterialHelper::isButtonCompatible)) { Stargate.logSevere(String.format(failString, "Gate button must be a type of button.")); return false; } - if (!gate.getPortalOpenBlock().isBlock()) { + if (checkMaterialPredicateFail(gate.getPortalOpenMaterials(), Material::isBlock)) { Stargate.logSevere(String.format(failString, "Gate open block must be a type of block.")); return false; } - if (!gate.getPortalClosedBlock().isBlock()) { + if (checkMaterialPredicateFail(gate.getPortalClosedMaterials(), Material::isBlock)) { Stargate.logSevere(String.format(failString, "Gate closed block must be a type of block.")); return false; } - for (Material material : gate.getCharacterMaterialMap().values()) { - if (!material.isBlock()) { + for (List materialSpecifiers : gate.getCharacterMaterialMap().values()) { + if (checkMaterialPredicateFail(materialSpecifiers, Material::isBlock)) { Stargate.logSevere(String.format(failString, "Every gate border block must be a type of block.")); return false; } @@ -220,12 +241,31 @@ public class GateHandler { return true; } + /** + * Checks whether a predicate is true for a list of material specifiers + * + * @param materialSpecifiers

The material specifiers to test

+ * @param predicate

The predicate to test

+ * @return

True if the predicate failed for any specified materials

+ */ + private static boolean checkMaterialPredicateFail(@NotNull List materialSpecifiers, + @NotNull Predicate predicate) { + Set closedMaterials = MaterialHelper.specifiersToMaterials(materialSpecifiers); + for (Material material : closedMaterials) { + if (!predicate.test(material)) { + return true; + } + } + + return false; + } + /** * Loads all gates inside the given folder * * @param gateFolder

The folder containing the gates

*/ - public static void loadGates(String gateFolder) { + public static void loadGates(@NotNull String gateFolder) { File directory = new File(gateFolder); File[] files; @@ -258,11 +298,12 @@ public class GateHandler { * * @param gateFolder

The folder containing gate config files

*/ - public static void writeDefaultGatesToFolder(String gateFolder) { + public static void writeDefaultGatesToFolder(@NotNull String gateFolder) { loadGateFromJar("nethergate.gate", gateFolder); loadGateFromJar("watergate.gate", gateFolder); loadGateFromJar("endgate.gate", gateFolder); loadGateFromJar("squarenetherglowstonegate.gate", gateFolder); + loadGateFromJar("wool.gate", gateFolder); } /** @@ -271,7 +312,7 @@ public class GateHandler { * @param gateFile

The name of the gate file

* @param gateFolder

The folder containing gates

*/ - private static void loadGateFromJar(String gateFile, String gateFolder) { + private static void loadGateFromJar(@NotNull String gateFile, @NotNull String gateFolder) { //Get an input stream for the internal file InputStream gateFileStream = Gate.class.getResourceAsStream("/gates/" + gateFile); if (gateFileStream != null) { @@ -293,7 +334,8 @@ public class GateHandler { * @param block

The control block to check

* @return

A list of gates using the given control block

*/ - public static Gate[] getGatesByControlBlock(Block block) { + @NotNull + public static List getGatesByControlBlock(@NotNull Block block) { return getGatesByControlBlock(block.getType()); } @@ -306,12 +348,24 @@ public class GateHandler { * @param type

The type of the control block to check

* @return

A list of gates using the given material for control block

*/ - public static Gate[] getGatesByControlBlock(Material type) { - Gate[] result = new Gate[0]; - List lookup = controlBlocks.get(type); + @NotNull + public static List getGatesByControlBlock(@NotNull Material type) { + List result = new ArrayList<>(); + List fromId = controlBlocks.get(type); + List fromTag = null; + for (String tagString : controlBlockTags.keySet()) { + Tag tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(tagString.replaceFirst( + "minecraft:", "")), Material.class); + if (tag != null && tag.isTagged(type)) { + fromTag = controlBlockTags.get(tag.getKey().toString()); + } + } - if (lookup != null) { - result = lookup.toArray(result); + if (fromId != null) { + result.addAll(fromId); + } + if (fromTag != null) { + result.addAll(fromTag); } return result; @@ -323,7 +377,8 @@ public class GateHandler { * @param fileName

The filename of the gate to get

* @return

The gate with the given filename

*/ - public static Gate getGateByName(String fileName) { + @Nullable + public static Gate getGateByName(@NotNull String fileName) { return gates.get(fileName); } @@ -342,6 +397,7 @@ public class GateHandler { public static void clearGates() { gates.clear(); controlBlocks.clear(); + controlBlockTags.clear(); } } diff --git a/src/main/java/net/knarcraft/stargate/portal/property/gate/GateLayout.java b/src/main/java/net/knarcraft/stargate/portal/property/gate/GateLayout.java index 540983e..1940471 100644 --- a/src/main/java/net/knarcraft/stargate/portal/property/gate/GateLayout.java +++ b/src/main/java/net/knarcraft/stargate/portal/property/gate/GateLayout.java @@ -1,6 +1,8 @@ package net.knarcraft.stargate.portal.property.gate; import net.knarcraft.stargate.container.RelativeBlockVector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.BufferedWriter; import java.io.IOException; @@ -28,7 +30,7 @@ public class GateLayout { * * @param layout

A character matrix describing the layout

*/ - public GateLayout(Character[][] layout) { + public GateLayout(@NotNull Character[][] layout) { this.layout = layout; readLayout(); } @@ -38,6 +40,7 @@ public class GateLayout { * * @return

The character array describing this layout

*/ + @NotNull public Character[][] getLayout() { return this.layout; } @@ -49,6 +52,7 @@ public class GateLayout { * * @return

The locations of entrances for this gate

*/ + @NotNull public RelativeBlockVector[] getEntrances() { return entrances; } @@ -61,6 +65,7 @@ public class GateLayout { * * @return

The locations of border blocks for this gate

*/ + @NotNull public RelativeBlockVector[] getBorder() { return border; } @@ -70,6 +75,7 @@ public class GateLayout { * * @return

The exit block defined in the layout

*/ + @Nullable public RelativeBlockVector getExit() { return exitBlock; } @@ -82,6 +88,7 @@ public class GateLayout { * * @return

All possible exits

*/ + @NotNull public List getExits() { return exits; } @@ -94,6 +101,7 @@ public class GateLayout { * * @return

The locations of the control blocks for this gate

*/ + @NotNull public RelativeBlockVector[] getControls() { return controls; } @@ -104,7 +112,7 @@ public class GateLayout { * @param bufferedWriter

The buffered writer to write to

* @throws IOException

If unable to write to the buffered writer

*/ - public void saveLayout(BufferedWriter bufferedWriter) throws IOException { + public void saveLayout(@NotNull BufferedWriter bufferedWriter) throws IOException { for (Character[] line : this.layout) { for (Character character : line) { bufferedWriter.append(character); @@ -137,8 +145,9 @@ public class GateLayout { * @param entranceList

The list of entrances to save to

* @param borderList

The list of border blocks to save to

*/ - private void readLayout(List controlList, List entranceList, - List borderList) { + private void readLayout(@NotNull List controlList, + @NotNull List entranceList, + @NotNull List borderList) { //Store the lowest opening for each column int[] exitDepths = new int[layout[0].length]; @@ -173,9 +182,10 @@ public class GateLayout { * @param entranceList

The list of entrances to save to

* @param borderList

The list of border blocks to save to

*/ - private void parseLayoutCharacter(Character key, int columnIndex, int rowIndex, int[] exitDepths, - List controlList, List entranceList, - List borderList) { + private void parseLayoutCharacter(@NotNull Character key, int columnIndex, int rowIndex, int[] exitDepths, + @NotNull List controlList, + @NotNull List entranceList, + @NotNull List borderList) { //Add control blocks to the control block list if (key.equals(GateHandler.getControlBlockCharacter())) { controlList.add(new RelativeBlockVector(columnIndex, rowIndex, 0)); @@ -202,7 +212,7 @@ public class GateLayout { * @param character

The character to check

* @return

True if the character represents an opening

*/ - private boolean isOpening(Character character) { + private boolean isOpening(@NotNull Character character) { return character.equals(GateHandler.getEntranceCharacter()) || character.equals(GateHandler.getExitCharacter()); } diff --git a/src/main/java/net/knarcraft/stargate/portal/teleporter/EntityTeleporter.java b/src/main/java/net/knarcraft/stargate/portal/teleporter/EntityTeleporter.java index e963c92..8b18d11 100644 --- a/src/main/java/net/knarcraft/stargate/portal/teleporter/EntityTeleporter.java +++ b/src/main/java/net/knarcraft/stargate/portal/teleporter/EntityTeleporter.java @@ -3,6 +3,7 @@ package net.knarcraft.stargate.portal.teleporter; import net.knarcraft.stargate.event.StargateEntityPortalEvent; import net.knarcraft.stargate.portal.Portal; import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; /** * The portal teleporter takes care of the actual portal teleportation for any entities @@ -16,7 +17,7 @@ public class EntityTeleporter extends Teleporter { * * @param targetPortal

The portal which is the target of the teleportation

*/ - public EntityTeleporter(Portal targetPortal, Entity teleportingEntity) { + public EntityTeleporter(@NotNull Portal targetPortal, @NotNull Entity teleportingEntity) { super(targetPortal, teleportingEntity); this.teleportingEntity = teleportingEntity; } @@ -27,7 +28,7 @@ public class EntityTeleporter extends Teleporter { * @param origin

The portal the entity is teleporting from

* @return

True if the entity was teleported. False otherwise

*/ - public boolean teleportEntity(Portal origin) { + public boolean teleportEntity(@NotNull Portal origin) { return teleport(origin, new StargateEntityPortalEvent(teleportingEntity, origin, portal, exit)); } diff --git a/src/main/java/net/knarcraft/stargate/portal/teleporter/PlayerTeleporter.java b/src/main/java/net/knarcraft/stargate/portal/teleporter/PlayerTeleporter.java index 3460340..e34a68c 100644 --- a/src/main/java/net/knarcraft/stargate/portal/teleporter/PlayerTeleporter.java +++ b/src/main/java/net/knarcraft/stargate/portal/teleporter/PlayerTeleporter.java @@ -10,6 +10,8 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -26,7 +28,7 @@ public class PlayerTeleporter extends Teleporter { * @param targetPortal

The portal which is the target of the teleportation

* @param player

The teleporting player

*/ - public PlayerTeleporter(Portal targetPortal, Player player) { + public PlayerTeleporter(@NotNull Portal targetPortal, @NotNull Player player) { super(targetPortal, player); this.player = player; } @@ -37,7 +39,7 @@ public class PlayerTeleporter extends Teleporter { * @param origin

The portal the player teleports from

* @param event

The player move event triggering the event

*/ - public void teleportPlayer(Portal origin, PlayerMoveEvent event) { + public void teleportPlayer(@NotNull Portal origin, @Nullable PlayerMoveEvent event) { double velocity = player.getVelocity().length(); List passengers = player.getPassengers(); diff --git a/src/main/java/net/knarcraft/stargate/portal/teleporter/Teleporter.java b/src/main/java/net/knarcraft/stargate/portal/teleporter/Teleporter.java index 7e1f7ee..9894d6b 100644 --- a/src/main/java/net/knarcraft/stargate/portal/teleporter/Teleporter.java +++ b/src/main/java/net/knarcraft/stargate/portal/teleporter/Teleporter.java @@ -20,6 +20,8 @@ import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -55,7 +57,7 @@ public abstract class Teleporter { * @param portal

The portal which is the target of the teleportation

* @param teleportedEntity

The entity teleported by this teleporter

*/ - public Teleporter(Portal portal, Entity teleportedEntity) { + protected Teleporter(@NotNull Portal portal, @NotNull Entity teleportedEntity) { this.portal = portal; this.scheduler = Stargate.getInstance().getServer().getScheduler(); this.teleportedEntity = teleportedEntity; @@ -69,11 +71,11 @@ public abstract class Teleporter { * @param stargateTeleportEvent

The event to call to make sure the teleportation is valid

* @return

True if the teleportation was successfully performed

*/ - public boolean teleport(Portal origin, StargateTeleportEvent stargateTeleportEvent) { + public boolean teleport(@NotNull Portal origin, @Nullable StargateTeleportEvent stargateTeleportEvent) { List passengers = teleportedEntity.getPassengers(); //Call the StargateEntityPortalEvent to allow plugins to change destination - if (!origin.equals(portal)) { + if (!origin.equals(portal) && stargateTeleportEvent != null) { exit = triggerPortalEvent(origin, stargateTeleportEvent); if (exit == null) { return false; @@ -96,6 +98,7 @@ public abstract class Teleporter { * * @return

The exit location of this teleporter

*/ + @NotNull public Location getExit() { return exit.clone(); } @@ -107,7 +110,9 @@ public abstract class Teleporter { * @param stargateTeleportEvent

The exit location to teleport the entity to

* @return

The location the entity should be teleported to, or null if the event was cancelled

*/ - protected Location triggerPortalEvent(Portal origin, StargateTeleportEvent stargateTeleportEvent) { + @Nullable + protected Location triggerPortalEvent(@NotNull Portal origin, + @NotNull StargateTeleportEvent stargateTeleportEvent) { Stargate.getInstance().getServer().getPluginManager().callEvent((Event) stargateTeleportEvent); //Teleport is cancelled. Teleport the entity back to where it came from just for sanity's sake if (stargateTeleportEvent.isCancelled()) { @@ -122,7 +127,7 @@ public abstract class Teleporter { * * @param exit

The location the entity will exit from

*/ - protected void adjustExitLocationRotation(Location exit) { + protected void adjustExitLocationRotation(@NotNull Location exit) { int adjust = 0; if (portal.getOptions().isBackwards()) { adjust = 180; @@ -151,7 +156,9 @@ public abstract class Teleporter { * @param entity

The travelling entity

* @return

A location which won't suffocate the entity inside the portal

*/ - private Location preventExitSuffocation(RelativeBlockVector relativeExit, Location exitLocation, Entity entity) { + @NotNull + private Location preventExitSuffocation(@NotNull RelativeBlockVector relativeExit, + @NotNull Location exitLocation, @NotNull Entity entity) { //Go left to find start of opening RelativeBlockVector openingLeft = getPortalExitEdge(relativeExit, -1); @@ -159,8 +166,8 @@ public abstract class Teleporter { RelativeBlockVector openingRight = getPortalExitEdge(relativeExit, 1); //Get the width to check if the entity fits - int openingWidth = openingRight.getRight() - openingLeft.getRight() + 1; - int existingOffset = relativeExit.getRight() - openingLeft.getRight(); + int openingWidth = openingRight.right() - openingLeft.right() + 1; + int existingOffset = relativeExit.right() - openingLeft.right(); double newOffset = (openingWidth - existingOffset) / 2D; //Remove the half offset for better centering @@ -180,7 +187,8 @@ public abstract class Teleporter { * @param entity

The entity to adjust the exit location for

* @return

The adjusted exit location

*/ - private Location moveExitLocationOutwards(Location exitLocation, Entity entity) { + @NotNull + private Location moveExitLocationOutwards(@NotNull Location exitLocation, @NotNull Entity entity) { double entitySize = EntityHelper.getEntityMaxSize(entity); int entityBoxSize = EntityHelper.getEntityMaxSizeInt(entity); if (entitySize > 1) { @@ -207,12 +215,13 @@ public abstract class Teleporter { * @param direction

The direction to move (+1 for right, -1 for left)

* @return

The right or left edge of the opening

*/ - private RelativeBlockVector getPortalExitEdge(RelativeBlockVector relativeExit, int direction) { + @NotNull + private RelativeBlockVector getPortalExitEdge(@NotNull RelativeBlockVector relativeExit, int direction) { RelativeBlockVector openingEdge = relativeExit; do { - RelativeBlockVector possibleOpening = new RelativeBlockVector(openingEdge.getRight() + direction, - openingEdge.getDown(), openingEdge.getOut()); + RelativeBlockVector possibleOpening = new RelativeBlockVector(openingEdge.right() + direction, + openingEdge.down(), openingEdge.out()); if (portal.getGate().getLayout().getExits().contains(possibleOpening)) { openingEdge = possibleOpening; } else { @@ -234,7 +243,8 @@ public abstract class Teleporter { * @param exitLocation

The exit location generated

* @return

The location the travelling entity should be teleported to

*/ - private Location adjustExitLocationHeight(Entity entity, Location exitLocation) { + @NotNull + private Location adjustExitLocationHeight(@NotNull Entity entity, @Nullable Location exitLocation) { if (exitLocation != null) { BlockData blockData = exitLocation.getBlock().getBlockData(); if ((blockData instanceof Bisected bisected && bisected.getHalf() == Bisected.Half.BOTTOM) || @@ -257,7 +267,8 @@ public abstract class Teleporter { * @param entity

The entity to teleport (used to determine distance from portal to avoid suffocation)

* @return

The location the entity should be teleported to.

*/ - private Location getExit(Entity entity) { + @NotNull + private Location getExit(@NotNull Entity entity) { Location exitLocation = null; RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit(); if (relativeExit != null) { @@ -270,12 +281,10 @@ public abstract class Teleporter { } exitLocation = exit.getRelativeLocation(0D, 0D, 1, portalYaw); - if (entity != null) { - double entitySize = EntityHelper.getEntityMaxSize(entity); - //Prevent exit suffocation for players riding horses or similar - if (entitySize > 1) { - exitLocation = preventExitSuffocation(relativeExit, exitLocation, entity); - } + double entitySize = EntityHelper.getEntityMaxSize(entity); + //Prevent exit suffocation for players riding horses or similar + if (entitySize > 1) { + exitLocation = preventExitSuffocation(relativeExit, exitLocation, entity); } } else { Stargate.logWarning(String.format("Missing destination point in .gate file %s", @@ -293,6 +302,7 @@ public abstract class Teleporter { * * @return

A list of chunks to load

*/ + @NotNull private List getChunksToLoad() { List chunksToLoad = new ArrayList<>(); for (RelativeBlockVector vector : portal.getGate().getLayout().getEntrances()) { diff --git a/src/main/java/net/knarcraft/stargate/portal/teleporter/VehicleTeleporter.java b/src/main/java/net/knarcraft/stargate/portal/teleporter/VehicleTeleporter.java index 4f3e42c..da0a126 100644 --- a/src/main/java/net/knarcraft/stargate/portal/teleporter/VehicleTeleporter.java +++ b/src/main/java/net/knarcraft/stargate/portal/teleporter/VehicleTeleporter.java @@ -14,8 +14,10 @@ import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Vehicle; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import java.util.List; +import java.util.Objects; /** * The portal teleporter takes care of the actual portal teleportation for any vehicles @@ -30,7 +32,7 @@ public class VehicleTeleporter extends EntityTeleporter { * @param targetPortal

The targetPortal which is the target of the teleportation

* @param teleportingVehicle

The teleporting vehicle

*/ - public VehicleTeleporter(Portal targetPortal, Vehicle teleportingVehicle) { + public VehicleTeleporter(@NotNull Portal targetPortal, @NotNull Vehicle teleportingVehicle) { super(targetPortal, teleportingVehicle); this.teleportingVehicle = teleportingVehicle; } @@ -45,7 +47,7 @@ public class VehicleTeleporter extends EntityTeleporter { * @return

True if the vehicle was teleported. False otherwise

*/ @Override - public boolean teleportEntity(Portal origin) { + public boolean teleportEntity(@NotNull Portal origin) { Stargate.debug("VehicleTeleporter::teleport", "Preparing to teleport: " + teleportingVehicle); double velocity = teleportingVehicle.getVelocity().length(); @@ -75,7 +77,7 @@ public class VehicleTeleporter extends EntityTeleporter { * @param origin

The portal the vehicle teleported from

* @return

True if the vehicle was teleported. False otherwise

*/ - private boolean teleportVehicle(Location exit, Vector newVelocity, Portal origin) { + private boolean teleportVehicle(@NotNull Location exit, @NotNull Vector newVelocity, @NotNull Portal origin) { //Load chunks to make sure not to teleport to the void loadChunks(); @@ -113,7 +115,7 @@ public class VehicleTeleporter extends EntityTeleporter { * @param passengers

The passengers to teleport

* @return

True if the passengers are allowed to teleport

*/ - private boolean vehiclePassengersAllowed(List passengers) { + private boolean vehiclePassengersAllowed(@NotNull List passengers) { StargateGateConfig config = Stargate.getGateConfig(); //Don't teleport if the vehicle contains a creature and creature transportation is disabled if (TeleportHelper.containsNonPlayer(passengers) && !config.handleCreatureTransportation()) { @@ -131,7 +133,8 @@ public class VehicleTeleporter extends EntityTeleporter { * @param newVelocity

The new velocity of the teleported vehicle

* @param origin

The portal the vehicle teleported from

*/ - private void teleportVehicle(List passengers, Location exit, Vector newVelocity, Portal origin) { + private void teleportVehicle(@NotNull List passengers, @NotNull Location exit, @NotNull Vector newVelocity, + @NotNull Portal origin) { if (teleportingVehicle.eject()) { TeleportHelper.handleEntityPassengers(passengers, teleportingVehicle, origin, portal, exit.getDirection(), newVelocity); @@ -159,17 +162,18 @@ public class VehicleTeleporter extends EntityTeleporter { * @param newVelocity

The new velocity of the new vehicle

* @param origin

The portal the vehicle teleported from

*/ - private void putPassengersInNewVehicle(List passengers, Location exit, - Vector newVelocity, Portal origin) { + private void putPassengersInNewVehicle(@NotNull List passengers, @NotNull Location exit, + @NotNull Vector newVelocity, Portal origin) { World vehicleWorld = exit.getWorld(); if (vehicleWorld == null) { Stargate.logWarning("Unable to get the world to teleport the vehicle to"); return; } //Spawn a new vehicle - Vehicle newVehicle = vehicleWorld.spawn(exit, teleportingVehicle.getClass()); + Vehicle newVehicle = (Vehicle) vehicleWorld.spawn(exit, + Objects.requireNonNull(teleportingVehicle.getType().getEntityClass())); if (teleportingVehicle instanceof Boat boat) { - ((Boat) newVehicle).setBoatType(boat.getBoatType()); + boat.setBoatType(boat.getBoatType()); } //Remove the old vehicle if (teleportingVehicle.eject()) { diff --git a/src/main/java/net/knarcraft/stargate/thread/BlockChangeThread.java b/src/main/java/net/knarcraft/stargate/thread/BlockChangeThread.java index faa14fc..089817a 100644 --- a/src/main/java/net/knarcraft/stargate/thread/BlockChangeThread.java +++ b/src/main/java/net/knarcraft/stargate/thread/BlockChangeThread.java @@ -8,11 +8,12 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.EndGateway; import org.bukkit.block.data.Orientable; +import org.jetbrains.annotations.NotNull; /** * This thread changes gate blocks to display a gate as open or closed * - *

This thread fetches some entries from blockPopulateQueue each time it's called.

+ *

This thread fetches some entries from blockChangeRequestQueue each time it's called.

*/ public class BlockChangeThread implements Runnable { @@ -34,7 +35,7 @@ public class BlockChangeThread implements Runnable { */ public static boolean pollQueue() { //Abort if there's no work to be done - BlockChangeRequest blockChangeRequest = Stargate.getBlockChangeRequestQueue().poll(); + BlockChangeRequest blockChangeRequest = Stargate.getControlBlockUpdateRequestQueue().poll(); if (blockChangeRequest == null) { return true; } @@ -58,7 +59,7 @@ public class BlockChangeThread implements Runnable { * * @param block

The block to fix

*/ - private static void fixEndGatewayGate(Block block) { + private static void fixEndGatewayGate(@NotNull Block block) { EndGateway gateway = (EndGateway) block.getState(); gateway.setAge(Long.MIN_VALUE); if (block.getWorld().getEnvironment() == World.Environment.THE_END) { @@ -74,7 +75,7 @@ public class BlockChangeThread implements Runnable { * @param block

The block to orient

* @param axis

The axis to use for orienting the block

*/ - private static void orientBlock(Block block, Axis axis) { + private static void orientBlock(@NotNull Block block, @NotNull Axis axis) { Orientable orientable = (Orientable) block.getBlockData(); orientable.setAxis(axis); block.setBlockData(orientable); diff --git a/src/main/java/net/knarcraft/stargate/thread/ControlBlocksUpdateThread.java b/src/main/java/net/knarcraft/stargate/thread/ControlBlocksUpdateThread.java new file mode 100644 index 0000000..00ec422 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/thread/ControlBlocksUpdateThread.java @@ -0,0 +1,50 @@ +package net.knarcraft.stargate.thread; + +import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.container.BlockChangeRequest; +import net.knarcraft.stargate.container.BlockLocation; +import net.knarcraft.stargate.container.ControlBlockUpdateRequest; +import net.knarcraft.stargate.portal.Portal; +import net.knarcraft.stargate.utility.DirectionHelper; +import net.knarcraft.stargate.utility.MaterialHelper; +import net.knarcraft.stargate.utility.PortalFileHelper; +import org.bukkit.Material; + +/** + * This thread updates the signs and buttons of Stargates, if deemed necessary + */ +public class ControlBlocksUpdateThread implements Runnable { + + @Override + public void run() { + //Abort if there's no work to be done + ControlBlockUpdateRequest controlBlockUpdateRequest = Stargate.getButtonUpdateRequestQueue().poll(); + if (controlBlockUpdateRequest == null) { + return; + } + + Portal portal = controlBlockUpdateRequest.portal(); + portal.drawSign(); + + BlockLocation buttonLocation = PortalFileHelper.getButtonLocation(portal); + if (buttonLocation == null) { + return; + } + + Stargate.debug("ControlBlocksUpdateThread", "Updating control blocks for portal " + portal); + + if (portal.getOptions().isAlwaysOn()) { + //Clear button if it exists + if (MaterialHelper.isButtonCompatible(buttonLocation.getType())) { + Material newMaterial = PortalFileHelper.decideRemovalMaterial(buttonLocation, portal); + Stargate.addControlBlockUpdateRequest(new BlockChangeRequest(buttonLocation, newMaterial, null)); + } + } else { + //Replace button if the material is not a button + if (!MaterialHelper.isButtonCompatible(buttonLocation.getType())) { + PortalFileHelper.generatePortalButton(portal, DirectionHelper.getBlockFaceFromYaw(portal.getYaw())); + } + } + } + +} diff --git a/src/main/java/net/knarcraft/stargate/utility/BStatsHelper.java b/src/main/java/net/knarcraft/stargate/utility/BStatsHelper.java new file mode 100644 index 0000000..134419b --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/utility/BStatsHelper.java @@ -0,0 +1,55 @@ +package net.knarcraft.stargate.utility; + +import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.ConfigOption; +import net.knarcraft.stargate.portal.PortalHandler; +import net.knarcraft.stargate.portal.property.gate.GateHandler; +import org.bstats.bukkit.Metrics; +import org.bstats.charts.SimplePie; +import org.bstats.charts.SingleLineChart; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; + +/** + * A helper for dealing with BStats + */ +public final class BStatsHelper { + + private static boolean hasBeenInitialized = false; + + private BStatsHelper() { + + } + + /** + * Initializes BStats + * + * @param plugin

The plugin to initialize BStats for

+ */ + public static void initialize(@NotNull JavaPlugin plugin) { + if (hasBeenInitialized) { + throw new IllegalArgumentException("BStats initialized twice"); + } else { + hasBeenInitialized = true; + } + + int pluginId = 10451; + Metrics metrics = new Metrics(plugin, pluginId); + + Map configValues = Stargate.getStargateConfig().getConfigOptions(); + Map> portalNetworks = PortalHandler.getAllPortalNetworks(); + int totalPortals = 0; + for (List portals : portalNetworks.values()) { + totalPortals += portals.size(); + } + + metrics.addCustomChart(new SimplePie("language", () -> (String) configValues.get(ConfigOption.LANGUAGE))); + metrics.addCustomChart(new SimplePie("gateformats", () -> String.valueOf(GateHandler.getGateCount()))); + int finalTotalPortals = totalPortals; + metrics.addCustomChart(new SingleLineChart("gatesv3", () -> finalTotalPortals)); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java b/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java index 677e80f..8430c95 100644 --- a/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java @@ -1,12 +1,15 @@ package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter; -import net.md_5.bungee.api.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerMoveEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -36,6 +39,7 @@ public final class BungeeHelper { * * @return

The bungee plugin channel

*/ + @NotNull public static String getBungeeChannel() { return bungeeChannel; } @@ -48,9 +52,10 @@ public final class BungeeHelper { * queue and teleported to the destination.

* * @param playerUUID

The UUID of the player to remove

- * @return

The name of the destination portal the player should be teleported to

+ * @return

The name of the destination portal the player should be teleported to, or null if not queued

*/ - public static String removeFromQueue(UUID playerUUID) { + @Nullable + public static String removeFromQueue(@NotNull UUID playerUUID) { return bungeeQueue.remove(playerUUID); } @@ -61,7 +66,7 @@ public final class BungeeHelper { * @param entrancePortal

The portal the player is teleporting from

* @return

True if the message was successfully sent

*/ - public static boolean sendTeleportationMessage(Player player, Portal entrancePortal) { + public static boolean sendTeleportationMessage(@NotNull Player player, @NotNull Portal entrancePortal) { try { //Build the teleportation message, format is delimiter String message = player.getUniqueId() + teleportMessageDelimiter + entrancePortal.getDestinationName(); @@ -72,7 +77,7 @@ public final class BungeeHelper { //Build the message data and send it over the SGBungee BungeeCord channel dataOutputStream.writeUTF("Forward"); //Send the message to the server defined in the entrance portal's network line - dataOutputStream.writeUTF(stripColor(entrancePortal.getNetwork())); + dataOutputStream.writeUTF(Portal.cleanString(entrancePortal.getNetwork())); //Specify the sub-channel/tag to make it recognizable on arrival dataOutputStream.writeUTF(bungeeSubChannel); //Write the length of the message @@ -81,9 +86,8 @@ public final class BungeeHelper { dataOutputStream.writeBytes(message); //Send the plugin message player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray()); - } catch (IOException ex) { - Stargate.logSevere("Error sending BungeeCord teleport packet"); - ex.printStackTrace(); + } catch (IOException exception) { + Stargate.logSevere("Error sending BungeeCord teleport packet! Message: " + exception.getMessage()); return false; } return true; @@ -96,20 +100,19 @@ public final class BungeeHelper { * @param entrancePortal

The bungee portal the player is teleporting from

* @return

True if the plugin message was sent successfully

*/ - public static boolean changeServer(Player player, Portal entrancePortal) { + public static boolean changeServer(@NotNull Player player, @NotNull Portal entrancePortal) { try { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); //Send a connect-message to connect the player to the server defined in the entrance portal's network line dataOutputStream.writeUTF("Connect"); - dataOutputStream.writeUTF(stripColor(entrancePortal.getNetwork())); + dataOutputStream.writeUTF(Portal.cleanString(entrancePortal.getNetwork())); //Send the plugin message player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray()); - } catch (IOException ex) { - Stargate.logSevere("Error sending BungeeCord connect packet"); - ex.printStackTrace(); + } catch (IOException exception) { + Stargate.logSevere("Error sending BungeeCord connect packet! Message: " + exception.getMessage()); return false; } return true; @@ -121,6 +124,7 @@ public final class BungeeHelper { * @param message

The byte array to read

* @return

The message contained in the byte array, or null on failure

*/ + @Nullable public static String readPluginMessage(byte[] message) { byte[] data; try { @@ -137,9 +141,8 @@ public final class BungeeHelper { data = new byte[dataLength]; //Read the message to the prepared array dataInputStream.readFully(data); - } catch (IOException ex) { - Stargate.logSevere("Error receiving BungeeCord message"); - ex.printStackTrace(); + } catch (IOException exception) { + Stargate.logSevere("Error receiving BungeeCord message. Message: " + exception.getMessage()); return null; } return new String(data); @@ -150,7 +153,7 @@ public final class BungeeHelper { * * @param receivedMessage

The received teleport message

*/ - public static void handleTeleportMessage(String receivedMessage) { + public static void handleTeleportMessage(@NotNull String receivedMessage) { //Get the player id and destination from the message String[] messageParts = receivedMessage.split(teleportMessageDelimiter); UUID playerUUID = UUID.fromString(messageParts[0]); @@ -179,11 +182,12 @@ public final class BungeeHelper { * @param event

The event causing the teleportation

* @return

True if the teleportation was successful

*/ - public static boolean bungeeTeleport(Player player, Portal entrancePortal, PlayerMoveEvent event) { + public static boolean bungeeTeleport(@NotNull Player player, @NotNull Portal entrancePortal, + @NotNull PlayerMoveEvent event) { //Check if bungee is actually enabled if (!Stargate.getGateConfig().enableBungee()) { - if (!entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled")); + if (!entrancePortal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.BUNGEE_DISABLED).error(player); } entrancePortal.getPortalOpener().closePortal(false); return false; @@ -208,14 +212,4 @@ public final class BungeeHelper { return true; } - /** - * Strips all color tags from a string - * - * @param string

The string to strip color from

- * @return

The string without color codes

- */ - private static String stripColor(String string) { - return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', string)); - } - } diff --git a/src/main/java/net/knarcraft/stargate/utility/DirectionHelper.java b/src/main/java/net/knarcraft/stargate/utility/DirectionHelper.java index c42fe6d..0300cc4 100644 --- a/src/main/java/net/knarcraft/stargate/utility/DirectionHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/DirectionHelper.java @@ -3,6 +3,7 @@ package net.knarcraft.stargate.utility; import org.bukkit.Location; import org.bukkit.block.BlockFace; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; /** * This class helps with direction-related calculations @@ -24,7 +25,7 @@ public final class DirectionHelper { * @param location2

The second location, which the yaw will point towards

* @return

The yaw pointing from the first location to the second location

*/ - public static float getYawFromLocationDifference(Location location1, Location location2) { + public static float getYawFromLocationDifference(@NotNull Location location1, @NotNull Location location2) { Location difference = location1.clone().subtract(location2.clone()); if (difference.getX() > 0) { return 90; @@ -45,8 +46,10 @@ public final class DirectionHelper { * * @param yaw

The yaw value to convert

* @return

The block face the yaw corresponds to

+ * @throws IllegalArgumentException

If a yaw not divisible by 90 us given

*/ - public static BlockFace getBlockFaceFromYaw(double yaw) { + @NotNull + public static BlockFace getBlockFaceFromYaw(double yaw) throws IllegalArgumentException { //Make sure the yaw is between 0 and 360 yaw = normalizeYaw(yaw); @@ -68,8 +71,10 @@ public final class DirectionHelper { * * @param yaw

The yaw to convert to a direction vector

* @return

The direction vector pointing in the same direction as the yaw

+ * @throws IllegalArgumentException

If a yaw not divisible by 90 is given

*/ - public static Vector getDirectionVectorFromYaw(double yaw) { + @NotNull + public static Vector getDirectionVectorFromYaw(double yaw) throws IllegalArgumentException { //Make sure the yaw is between 0 and 360 yaw = normalizeYaw(yaw); @@ -99,7 +104,8 @@ public final class DirectionHelper { * @param yaw

The yaw when looking directly outwards from a portal

* @return

A location relative to the given location

*/ - public static Location moveLocation(Location location, double right, double down, double out, double yaw) { + @NotNull + public static Location moveLocation(@NotNull Location location, double right, double down, double out, double yaw) { return location.add(getCoordinateVectorFromRelativeVector(right, down, out, yaw)); } @@ -111,8 +117,14 @@ public final class DirectionHelper { * @param out

The distance outward from the top-left origin

* @param yaw

The yaw when looking directly outwards from a portal

* @return

A normal vector

+ * @throws IllegalArgumentException

If a yaw not divisible by 90 is given

*/ - public static Vector getCoordinateVectorFromRelativeVector(double right, double down, double out, double yaw) { + @NotNull + public static Vector getCoordinateVectorFromRelativeVector(double right, double down, double out, + double yaw) throws IllegalArgumentException { + //Make sure the yaw is between 0 and 360 + yaw = normalizeYaw(yaw); + if (yaw == 0) { //South return new Vector(right, -down, out); diff --git a/src/main/java/net/knarcraft/stargate/utility/EconomyHelper.java b/src/main/java/net/knarcraft/stargate/utility/EconomyHelper.java index 6c58e52..db45457 100644 --- a/src/main/java/net/knarcraft/stargate/utility/EconomyHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/EconomyHelper.java @@ -1,13 +1,18 @@ package net.knarcraft.stargate.utility; -import net.knarcraft.knarlib.formatting.StringFormatter; +import net.knarcraft.knarlib.formatting.FormatBuilder; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.config.EconomyConfig; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.property.PortalOwner; import net.milkbowl.vault.economy.Economy; import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.UUID; @@ -28,7 +33,7 @@ public final class EconomyHelper { * @param cost

The cost of teleportation

* @return

False if payment was successful. True if the payment was unsuccessful

*/ - public static boolean cannotPayTeleportFee(Portal entrancePortal, Player player, int cost) { + public static boolean cannotPayTeleportFee(@NotNull Portal entrancePortal, @NotNull Player player, int cost) { boolean success; //Try to charge the player. Paying the portal owner is only possible if a UUID is available @@ -77,10 +82,8 @@ public final class EconomyHelper { * @param portalOwner

The owner of the portal

* @param earnings

The amount the owner earned

*/ - public static void sendObtainMessage(String portalName, Player portalOwner, int earnings) { - String obtainedMsg = Stargate.getString("ecoObtain"); - obtainedMsg = replacePlaceholders(obtainedMsg, portalName, earnings); - Stargate.getMessageSender().sendSuccessMessage(portalOwner, obtainedMsg); + public static void sendObtainMessage(@NotNull String portalName, @NotNull Player portalOwner, int earnings) { + replacePlaceholders(new SGFormatBuilder(Message.ECONOMY_OBTAINED), portalName, earnings).success(portalOwner); } /** @@ -90,10 +93,8 @@ public final class EconomyHelper { * @param player

The interacting player

* @param cost

The cost of the interaction

*/ - public static void sendDeductMessage(String portalName, Player player, int cost) { - String deductMsg = Stargate.getString("ecoDeduct"); - deductMsg = replacePlaceholders(deductMsg, portalName, cost); - Stargate.getMessageSender().sendSuccessMessage(player, deductMsg); + public static void sendDeductMessage(@NotNull String portalName, @NotNull Player player, int cost) { + replacePlaceholders(new SGFormatBuilder(Message.ECONOMY_DEDUCTED), portalName, cost).success(player); } /** @@ -103,10 +104,8 @@ public final class EconomyHelper { * @param player

The interacting player

* @param cost

The cost of the interaction

*/ - public static void sendInsufficientFundsMessage(String portalName, Player player, int cost) { - String inFundMsg = Stargate.getString("ecoInFunds"); - inFundMsg = replacePlaceholders(inFundMsg, portalName, cost); - Stargate.getMessageSender().sendErrorMessage(player, inFundMsg); + public static void sendInsufficientFundsMessage(@NotNull String portalName, @NotNull Player player, int cost) { + replacePlaceholders(new SGFormatBuilder(Message.ECONOMY_INSUFFICIENT), portalName, cost).error(player); } /** @@ -116,10 +115,8 @@ public final class EconomyHelper { * @param player

The player breaking the portal

* @param cost

The amount the user has to pay for destroying the portal. (expects a negative value)

*/ - public static void sendRefundMessage(String portalName, Player player, int cost) { - String refundMsg = Stargate.getString("ecoRefund"); - refundMsg = replacePlaceholders(refundMsg, portalName, -cost); - Stargate.getMessageSender().sendSuccessMessage(player, refundMsg); + public static void sendRefundMessage(@NotNull String portalName, @NotNull Player player, int cost) { + replacePlaceholders(new SGFormatBuilder(Message.ECONOMY_REFUNDED), portalName, -cost).success(player); } /** @@ -130,7 +127,7 @@ public final class EconomyHelper { * @param destination

The destination portal

* @return

The cost of using the portal

*/ - public static int getUseCost(Player player, Portal source, Portal destination) { + public static int getUseCost(@NotNull Player player, @NotNull Portal source, @Nullable Portal destination) { EconomyConfig config = Stargate.getEconomyConfig(); //No payment required if (!config.useEconomy() || source.getOptions().isFree()) { @@ -160,7 +157,7 @@ public final class EconomyHelper { * @param cost

The cost of the transaction

* @return

True if the player was charged successfully

*/ - public static boolean chargePlayerIfNecessary(Player player, UUID target, int cost) { + public static boolean chargePlayerIfNecessary(@NotNull Player player, @NotNull UUID target, int cost) { if (skipPayment(cost)) { return true; } @@ -175,17 +172,43 @@ public final class EconomyHelper { * @param amount

The amount to charge

* @return

True if the payment succeeded, or if no payment was necessary

*/ - private static boolean chargePlayer(Player player, double amount) { + private static boolean chargePlayer(@NotNull Player player, double amount) { Economy economy = Stargate.getEconomyConfig().getEconomy(); if (Stargate.getEconomyConfig().isEconomyEnabled() && economy != null) { if (!economy.has(player, amount)) { return false; } - economy.withdrawPlayer(player, amount); + if (amount > 0) { + economy.withdrawPlayer(player, amount); + } else { + economy.depositPlayer(player, -amount); + } } return true; } + /** + * Transfers the given fees to the tax account + * + * @param economy

The economy to use

+ * @param cost

The cost to transfer

+ */ + @SuppressWarnings("deprecation") + private static void transferFees(@NotNull Economy economy, int cost) { + String accountName = Stargate.getEconomyConfig().getTaxAccount(); + if (accountName == null || accountName.isEmpty()) { + return; + } + + try { + UUID accountId = UUID.fromString(accountName); + OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(accountId); + economy.depositPlayer(offlinePlayer, cost); + } catch (IllegalArgumentException exception) { + economy.depositPlayer(accountName, cost); + } + } + /** * Charges the player for an action, if required * @@ -193,12 +216,20 @@ public final class EconomyHelper { * @param cost

The cost of the transaction

* @return

True if the player was charged successfully

*/ - public static boolean chargePlayerIfNecessary(Player player, int cost) { + public static boolean chargePlayerIfNecessary(@NotNull Player player, int cost) { if (skipPayment(cost)) { return true; } //Charge player - return chargePlayer(player, cost); + boolean charged = chargePlayer(player, cost); + + // Transfer the charged amount to the tax account + Economy economy = Stargate.getEconomyConfig().getEconomy(); + if (charged && economy != null) { + transferFees(economy, cost); + } + + return charged; } /** @@ -219,9 +250,10 @@ public final class EconomyHelper { * @param amount

The amount to charge

* @return

True if the payment succeeded, or if no payment was necessary

*/ - private static boolean chargePlayer(Player player, UUID target, double amount) { + private static boolean chargePlayer(@NotNull Player player, @NotNull UUID target, double amount) { Economy economy = Stargate.getEconomyConfig().getEconomy(); - if (Stargate.getEconomyConfig().isEconomyEnabled() && player.getUniqueId().compareTo(target) != 0 && economy != null) { + if (Stargate.getEconomyConfig().isEconomyEnabled() && player.getUniqueId().compareTo(target) != 0 && + economy != null) { if (!economy.has(player, amount)) { return false; } @@ -233,16 +265,16 @@ public final class EconomyHelper { } /** - * Replaces the cost and portal variables in a string + * Replaces the cost and portal variables in a format builder * - * @param message

The message to replace variables in

+ * @param builder

The format builder to replace variables for

* @param portalName

The name of the relevant portal

* @param cost

The cost for a given interaction

- * @return

The same string with cost and portal variables replaced

+ * @return

The same format builder

*/ - private static String replacePlaceholders(String message, String portalName, int cost) { - return StringFormatter.replacePlaceholders(message, new String[]{"%cost%", "%portal%"}, - new String[]{Stargate.getEconomyConfig().format(cost), portalName}); + private static FormatBuilder replacePlaceholders(@NotNull FormatBuilder builder, @NotNull String portalName, int cost) { + builder.replace("%cost%", Stargate.getEconomyConfig().format(cost)).replace("%portal%", portalName); + return builder; } } diff --git a/src/main/java/net/knarcraft/stargate/utility/EntityHelper.java b/src/main/java/net/knarcraft/stargate/utility/EntityHelper.java index 04ed19f..72adf56 100644 --- a/src/main/java/net/knarcraft/stargate/utility/EntityHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/EntityHelper.java @@ -1,6 +1,7 @@ package net.knarcraft.stargate.utility; import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; /** * This helper class helps with entity properties not immediately available @@ -21,7 +22,7 @@ public final class EntityHelper { * @param entity

The entity to get max size for

* @return

The max size of the entity

*/ - public static int getEntityMaxSizeInt(Entity entity) { + public static int getEntityMaxSizeInt(@NotNull Entity entity) { return (int) Math.ceil((float) getEntityMaxSize(entity)); } @@ -31,7 +32,7 @@ public final class EntityHelper { * @param entity

The entity to get max size for

* @return

The max size of the entity

*/ - public static double getEntityMaxSize(Entity entity) { + public static double getEntityMaxSize(@NotNull Entity entity) { return Math.max(entity.getBoundingBox().getWidthX(), entity.getBoundingBox().getWidthZ()); } diff --git a/src/main/java/net/knarcraft/stargate/utility/GateReader.java b/src/main/java/net/knarcraft/stargate/utility/GateReader.java index 0334019..fc1803a 100644 --- a/src/main/java/net/knarcraft/stargate/utility/GateReader.java +++ b/src/main/java/net/knarcraft/stargate/utility/GateReader.java @@ -1,13 +1,16 @@ package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier; +import net.knarcraft.stargate.config.material.MaterialSpecifier; import org.bukkit.Material; +import org.bukkit.configuration.InvalidConfigurationException; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Scanner; -import java.util.Set; /** * Helper class for reading gate files @@ -25,15 +28,16 @@ public final class GateReader { * @param characterMaterialMap

The map of characters to store valid symbols in

* @param fileName

The filename of the loaded gate config file

* @param design

The list to store the loaded design/layout to

- * @param frameTypes

The set to store frame/border materials to

* @param config

The map of config values to store to

* @return

The column count/width of the loaded gate

*/ - public static int readGateFile(Scanner scanner, Map characterMaterialMap, String fileName, - List> design, Set frameTypes, Map config) { + public static int readGateFile(@NotNull Scanner scanner, + @NotNull Map> characterMaterialMap, + @NotNull String fileName, @NotNull List> design, + @NotNull Map config) { boolean designing = false; int columns = 0; - try { + try (scanner) { while (scanner.hasNextLine()) { String line = scanner.nextLine(); @@ -46,7 +50,7 @@ public final class GateReader { } else { if (!line.isEmpty() && !line.startsWith("#")) { //Read a normal config value - readGateConfigValue(line, characterMaterialMap, frameTypes, config); + readGateConfigValue(line, characterMaterialMap, config); } else if ((line.isEmpty()) || (!line.contains("=") && !line.startsWith("#"))) { //An empty line marks the start of the gate's layout/design designing = true; @@ -56,10 +60,6 @@ public final class GateReader { } catch (Exception exception) { Stargate.logSevere(String.format("Could not load Gate %s - %s", fileName, exception.getMessage())); return -1; - } finally { - if (scanner != null) { - scanner.close(); - } } return columns; } @@ -77,8 +77,9 @@ public final class GateReader { * @param design

The two-dimensional list to store the loaded design to

* @return

The new max columns value of the design

*/ - private static int readGateDesignLine(String line, int maxColumns, Map characterMaterialMap, - String fileName, List> design) { + private static int readGateDesignLine(@NotNull String line, int maxColumns, + @NotNull Map> characterMaterialMap, + @NotNull String fileName, @NotNull List> design) { List row = new ArrayList<>(); //Update the max columns number if this line has more columns @@ -88,7 +89,7 @@ public final class GateReader { for (Character symbol : line.toCharArray()) { //Refuse read gate designs with unknown characters - if (symbol.equals('?') || (!characterMaterialMap.containsKey(symbol))) { + if (symbol.equals('?') || !characterMaterialMap.containsKey(symbol)) { Stargate.logSevere(String.format("Could not load Gate %s - Unknown symbol '%s' in diagram", fileName, symbol)); return -1; @@ -107,12 +108,12 @@ public final class GateReader { * * @param line

The line to read

* @param characterMaterialMap

The character to material map to store to

- * @param frameTypes

The set to store gate frame/border types to

* @param config

The config value map to store to

- * @throws Exception

If an invalid material is encountered

+ * @throws InvalidConfigurationException

If an invalid material is encountered

*/ - private static void readGateConfigValue(String line, Map characterMaterialMap, - Set frameTypes, Map config) throws Exception { + private static void readGateConfigValue(@NotNull String line, + @NotNull Map> characterMaterialMap, + @NotNull Map config) throws InvalidConfigurationException { String[] split = line.split("="); String key = split[0].trim(); String value = split[1].trim(); @@ -120,14 +121,13 @@ public final class GateReader { if (key.length() == 1) { //Read a gate frame material Character symbol = key.charAt(0); - Material material = Material.getMaterial(value); - if (material == null) { - throw new Exception("Invalid material in line: " + line); + + List materials = MaterialHelper.parseTagsAndMaterials(value); + if (!materials.isEmpty()) { + characterMaterialMap.put(symbol, materials); + } else { + throw new InvalidConfigurationException("Invalid material in line: " + line); } - //Register the map between the read symbol and the corresponding material - characterMaterialMap.put(symbol, material); - //Save the material as one of the frame materials used for this kind of gate - frameTypes.add(material); } else { //Read a normal config value config.put(key, value); @@ -142,12 +142,13 @@ public final class GateReader { * @param key

The config key to read

* @return

The read value, or -1 if it could not be read

*/ - public static int readGateConfig(Map config, String fileName, String key) { + public static int readGateConfig(@NotNull Map config, @NotNull String fileName, + @NotNull String key) { if (config.containsKey(key)) { try { return Integer.parseInt(config.get(key)); - } catch (NumberFormatException ex) { - Stargate.logWarning(String.format("%s reading %s: %s is not numeric", ex.getClass().getName(), + } catch (NumberFormatException exception) { + Stargate.logWarning(String.format("%s reading %s: %s is not numeric", exception.getClass().getName(), fileName, key)); } } @@ -164,17 +165,18 @@ public final class GateReader { * @param defaultMaterial

The default material to use, in case the config is invalid

* @return

The material specified in the config, or the default material if it could not be read

*/ - public static Material readGateConfig(Map config, String fileName, String key, - Material defaultMaterial) { + @NotNull + public static List readGateConfig(@NotNull Map config, @NotNull String fileName, + @NotNull String key, @NotNull Material defaultMaterial) { if (config.containsKey(key)) { - Material material = Material.getMaterial(config.get(key)); - if (material != null) { - return material; + List materialSpecifiers = MaterialHelper.parseTagsAndMaterials(config.get(key)); + if (!materialSpecifiers.isEmpty()) { + return materialSpecifiers; } else { Stargate.logWarning(String.format("Error reading %s: %s is not a material", fileName, key)); } } - return defaultMaterial; + return List.of(new BukkitMaterialSpecifier(defaultMaterial)); } /** @@ -187,7 +189,8 @@ public final class GateReader { * @param columns

The largest amount of columns in the design

* @return

A matrix containing the gate's layout

*/ - public static Character[][] generateLayoutMatrix(List> design, int columns) { + @NotNull + public static Character[][] generateLayoutMatrix(@NotNull List> design, int columns) { Character[][] layout = new Character[design.size()][columns]; for (int lineIndex = 0; lineIndex < design.size(); lineIndex++) { List row = design.get(lineIndex); diff --git a/src/main/java/net/knarcraft/stargate/utility/ListHelper.java b/src/main/java/net/knarcraft/stargate/utility/ListHelper.java new file mode 100644 index 0000000..fa82656 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/utility/ListHelper.java @@ -0,0 +1,28 @@ +package net.knarcraft.stargate.utility; + +import java.util.List; +import java.util.Random; + +/** + * A helper class for dealing with lists + */ +public final class ListHelper { + + private static final Random random = new Random(); + + private ListHelper() { + + } + + /** + * Gets a random item from a list + * + * @param list

The list to get an item from

+ * @param

The type of item the list contains

+ * @return

A random item

+ */ + public static T getRandom(List list) { + return list.get(random.nextInt(list.size())); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/utility/MaterialHelper.java b/src/main/java/net/knarcraft/stargate/utility/MaterialHelper.java index 1d00239..2e310bc 100644 --- a/src/main/java/net/knarcraft/stargate/utility/MaterialHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/MaterialHelper.java @@ -1,7 +1,19 @@ package net.knarcraft.stargate.utility; +import net.knarcraft.stargate.config.material.BukkitMaterialSpecifier; +import net.knarcraft.stargate.config.material.BukkitTagSpecifier; +import net.knarcraft.stargate.config.material.MaterialSpecifier; +import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * This class helps decide properties of materials not already present in the Spigot API @@ -18,7 +30,7 @@ public final class MaterialHelper { * @param material

The material to check

* @return

True if the material is a wall coral

*/ - public static boolean isWallCoral(Material material) { + public static boolean isWallCoral(@NotNull Material material) { //Unfortunately, there is no tag for dead wall corals, so they need to be checked manually return Tag.WALL_CORALS.isTagged(material) || material.equals(Material.DEAD_BRAIN_CORAL_WALL_FAN) || @@ -34,7 +46,7 @@ public final class MaterialHelper { * @param material

The material to check

* @return

True if the material is a container

*/ - public static boolean isContainer(Material material) { + public static boolean isContainer(@NotNull Material material) { return Tag.SHULKER_BOXES.isTagged(material) || material == Material.CHEST || material == Material.TRAPPED_CHEST || material == Material.ENDER_CHEST; } @@ -45,8 +57,84 @@ public final class MaterialHelper { * @param material

The material to check

* @return

True if the material can be used as a button

*/ - public static boolean isButtonCompatible(Material material) { + public static boolean isButtonCompatible(@NotNull Material material) { return Tag.BUTTONS.isTagged(material) || isWallCoral(material) || isContainer(material); } + @NotNull + public static String specifiersToString(@NotNull List specifiers) { + List names = new ArrayList<>(); + for (MaterialSpecifier specifier : specifiers) { + names.add(specifier.asString()); + } + return String.join(",", names); + } + + /** + * Converts a list of material specifiers to a set of materials + * + * @param specifiers

The material specifiers to convert

+ * @return

The materials the specifiers represent

+ */ + @NotNull + public static Set specifiersToMaterials(@NotNull List specifiers) { + Set output = new HashSet<>(); + + for (MaterialSpecifier specifier : specifiers) { + output.addAll(specifier.asMaterials()); + } + + return output; + } + + /** + * Parses all materials and material tags found in the input string + * + * @param input

The input string to parse

+ * @return

All material specifiers found

+ */ + @NotNull + public static List parseTagsAndMaterials(@NotNull String input) { + List specifiers = new ArrayList<>(); + + // Nothing to parse + if (input.isBlank()) { + return specifiers; + } + + String[] parts; + if (input.contains(",")) { + parts = input.split(","); + } else { + parts = new String[]{input}; + } + + for (String part : parts) { + MaterialSpecifier materialSpecifier = parseTagOrMaterial(part.trim()); + if (materialSpecifier != null) { + specifiers.add(materialSpecifier); + } + } + + return specifiers; + } + + @Nullable + private static MaterialSpecifier parseTagOrMaterial(@NotNull String input) { + if (input.startsWith("#")) { + String tagString = input.replaceFirst("#", "").toLowerCase(); + Tag tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(tagString), Material.class); + if (tag != null) { + return new BukkitTagSpecifier(tag); + } + } else { + Material material = Material.matchMaterial(input); + if (material != null) { + return new BukkitMaterialSpecifier(material); + } + } + + return null; + } + } diff --git a/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java b/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java index f87dc41..38e7a51 100644 --- a/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java @@ -1,12 +1,16 @@ package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.event.StargateAccessEvent; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.property.PortalOption; import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerMoveEvent; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static net.knarcraft.stargate.Stargate.getMaxNameNetworkLength; @@ -25,7 +29,7 @@ public final class PermissionHelper { * @param player

The player opening the portal

* @param portal

The portal to open

*/ - public static void openPortal(Player player, Portal portal) { + public static void openPortal(@NotNull Player player, @NotNull Portal portal) { Portal destination = portal.getPortalActivator().getDestination(); //For an always open portal, no action is necessary @@ -35,8 +39,8 @@ public final class PermissionHelper { //Destination is invalid or the same portal. Send an error message if (destination == null || destination == portal) { - if (!portal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("invalidMsg")); + if (!portal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.INVALID_DESTINATION).error(player); } return; } @@ -50,28 +54,8 @@ public final class PermissionHelper { return; } - //Deny access if another player has activated the portal, and it's still in use - if (!portal.getOptions().isFixed() && portal.getPortalActivator().isActive() && - portal.getActivePlayer() != player) { - if (!portal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); - } - return; - } - - //Check if the player can use the private gate - if (portal.getOptions().isPrivate() && !PermissionHelper.canUsePrivatePortal(player, portal)) { - if (!portal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); - } - return; - } - - //Destination is currently in use by another player, blocking teleportation - if (destination.isOpen() && !destination.getOptions().isAlwaysOn()) { - if (!portal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("blockMsg")); - } + // Check if the player is able to open the portal + if (canNotOpen(player, portal, destination)) { return; } @@ -79,6 +63,43 @@ public final class PermissionHelper { portal.getPortalOpener().openPortal(player, false); } + /** + * Checks whether something prevents the player from opening the given portal to the given destination + * + * @param player

The player trying to open the portal

+ * @param portal

The portal to open

+ * @param destination

The destination the player is attempting to open

+ * @return

True if the player cannot open the portal

+ */ + private static boolean canNotOpen(Player player, Portal portal, Portal destination) { + //Deny access if another player has activated the portal, and it's still in use + if (!portal.getOptions().isFixed() && portal.getPortalActivator().isActive() && + portal.getActivePlayer() != player) { + if (!portal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.ACCESS_DENIED).error(player); + } + return true; + } + + //Check if the player can use the private gate + if (portal.getOptions().isPrivate() && !PermissionHelper.canUsePrivatePortal(player, portal)) { + if (!portal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.ACCESS_DENIED).error(player); + } + return true; + } + + //Destination is currently in use by another player, blocking teleportation + if (destination.isOpen() && !destination.getOptions().isAlwaysOn()) { + if (!portal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.DESTINATION_BLOCKED).error(player); + } + return true; + } + + return false; + } + /** * Creates a StargateAccessEvent and gets the updated deny value * @@ -89,7 +110,7 @@ public final class PermissionHelper { * @param deny

Whether the player's access has already been denied by a previous check

* @return

False if the player should be allowed through the portal

*/ - public static boolean portalAccessDenied(Player player, Portal portal, boolean deny) { + public static boolean portalAccessDenied(@NotNull Player player, @NotNull Portal portal, boolean deny) { StargateAccessEvent event = new StargateAccessEvent(player, portal, deny); Stargate.getInstance().getServer().getPluginManager().callEvent(event); return event.getDeny(); @@ -103,23 +124,32 @@ public final class PermissionHelper { * @param destination

The portal the user wants to exit from

* @return

False if the user is allowed to access the portal

*/ - public static boolean cannotAccessPortal(Player player, Portal entrancePortal, Portal destination) { + public static boolean cannotAccessPortal(@NotNull Player player, @NotNull Portal entrancePortal, + @Nullable Portal destination) { boolean deny = false; + String route = "PermissionHelper::cannotAccessPortal"; if (entrancePortal.getOptions().isBungee()) { if (!PermissionHelper.canAccessServer(player, entrancePortal.getCleanNetwork())) { //If the portal is a bungee portal, and the player cannot access the server, deny - Stargate.debug("cannotAccessPortal", "Cannot access server"); + Stargate.debug(route, "Cannot access server"); deny = true; } } else if (PermissionHelper.cannotAccessNetwork(player, entrancePortal.getCleanNetwork())) { //If the player does not have access to the network, deny - Stargate.debug("cannotAccessPortal", "Cannot access network"); - deny = true; - } else if (PermissionHelper.cannotAccessWorld(player, destination.getWorld().getName())) { - //If the player does not have access to the portal's world, deny - Stargate.debug("cannotAccessPortal", "Cannot access world"); + Stargate.debug(route, "Cannot access network"); deny = true; + } else { + if (destination == null) { + //If there is no destination, deny + Stargate.debug(route, "Portal has no destination"); + deny = true; + } else if (destination.getWorld() != null && + PermissionHelper.cannotAccessWorld(player, destination.getWorld().getName())) { + //If the player does not have access to the portal's world, deny + Stargate.debug(route, "Cannot access world"); + deny = true; + } } //Allow other plugins to override whether the player can access the portal return portalAccessDenied(player, entrancePortal, deny); @@ -134,7 +164,7 @@ public final class PermissionHelper { * @param permission

The permission to check

* @return

True if the player has the permission

*/ - public static boolean hasPermission(Player player, String permission) { + public static boolean hasPermission(@NotNull Player player, @NotNull String permission) { if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) { Stargate.debug("hasPerm::Permission(" + player.getName() + ")", permission + " => " + player.hasPermission(permission)); @@ -152,7 +182,7 @@ public final class PermissionHelper { * @param permission

The permission to check

* @return

True if the player has the permission implicitly or explicitly

*/ - public static boolean hasPermissionImplicit(Player player, String permission) { + public static boolean hasPermissionImplicit(@NotNull Player player, @NotNull String permission) { if (!player.isPermissionSet(permission)) { if (Stargate.getStargateConfig().isPermissionDebuggingEnabled()) { Stargate.debug("hasPermissionImplicit::Permission", permission + " => implicitly true"); @@ -173,7 +203,7 @@ public final class PermissionHelper { * @param world

The world the player is trying to access

* @return

False if the player should be allowed to access the world

*/ - public static boolean cannotAccessWorld(Player player, String world) { + public static boolean cannotAccessWorld(@NotNull Player player, @NotNull String world) { //The player can access all worlds if (hasPermission(player, "stargate.world")) { //Check if the world permission has been explicitly denied @@ -190,7 +220,7 @@ public final class PermissionHelper { * @param network

The network to check

* @return

True if the player is denied from accessing the network

*/ - public static boolean cannotAccessNetwork(Player player, String network) { + public static boolean cannotAccessNetwork(@NotNull Player player, @NotNull String network) { //The player can access all networks if (hasPermission(player, "stargate.network")) { //Check if the world permission has been explicitly denied @@ -215,7 +245,7 @@ public final class PermissionHelper { * @param server

The server the player is trying to connect to

* @return

True if the player is allowed to access the given server

*/ - public static boolean canAccessServer(Player player, String server) { + public static boolean canAccessServer(@NotNull Player player, @NotNull String server) { //The player can access all servers if (hasPermission(player, "stargate.server")) { //Check if the server permission has been explicitly denied @@ -233,7 +263,7 @@ public final class PermissionHelper { * @param dest

The portal the player wants to teleport to

* @return

True if the player can travel for free

*/ - public static boolean isFree(Player player, Portal src, Portal dest) { + public static boolean isFree(@NotNull Player player, @NotNull Portal src, @Nullable Portal dest) { //This portal is free if (src.getOptions().isFree()) { return true; @@ -255,7 +285,7 @@ public final class PermissionHelper { * @param portal

The portal to check

* @return

True if the given player can see the given portal

*/ - public static boolean canSeePortal(Player player, Portal portal) { + public static boolean canSeePortal(@NotNull Player player, @NotNull Portal portal) { //The portal is not hidden if (!portal.getOptions().isHidden()) { return true; @@ -275,7 +305,7 @@ public final class PermissionHelper { * @param portal

The private portal used

* @return

True if the player is allowed to use the portal

*/ - public static boolean canUsePrivatePortal(Player player, Portal portal) { + public static boolean canUsePrivatePortal(@NotNull Player player, @NotNull Portal portal) { //Check if the player is the owner of the gate if (portal.isOwner(player)) { return true; @@ -291,7 +321,7 @@ public final class PermissionHelper { * @param option

The option the player is trying to use

* @return

True if the player is allowed to create a portal with the given option

*/ - public static boolean canUseOption(Player player, PortalOption option) { + public static boolean canUseOption(@NotNull Player player, @NotNull PortalOption option) { return hasPermission(player, option.getPermissionString()); } @@ -302,7 +332,7 @@ public final class PermissionHelper { * @param network

The network the player is trying to create a gate on

* @return

True if the player is allowed to create the new gate

*/ - public static boolean canCreateNetworkGate(Player player, String network) { + public static boolean canCreateNetworkGate(@NotNull Player player, @NotNull String network) { //Check if the player is allowed to create a portal on any network if (hasPermission(player, "stargate.create.network")) { //Check if the network has been explicitly denied @@ -318,7 +348,7 @@ public final class PermissionHelper { * @param player

The player trying to create the new gate

* @return

True if the player is allowed

*/ - public static boolean canCreatePersonalPortal(Player player) { + public static boolean canCreatePersonalPortal(@NotNull Player player) { return hasPermission(player, "stargate.create.personal"); } @@ -329,7 +359,7 @@ public final class PermissionHelper { * @param gate

The gate type of the new portal

* @return

True if the player is allowed to create a portal with the given gate layout

*/ - public static boolean canCreatePortal(Player player, String gate) { + public static boolean canCreatePortal(@NotNull Player player, @NotNull String gate) { //Check if the player is allowed to create all gates if (hasPermission(player, "stargate.create.gate")) { //Check if the gate type has been explicitly denied @@ -346,7 +376,7 @@ public final class PermissionHelper { * @param portal

The portal to destroy

* @return

True if the player is allowed to destroy the portal

*/ - public static boolean canDestroyPortal(Player player, Portal portal) { + public static boolean canDestroyPortal(@NotNull Player player, @NotNull Portal portal) { String network = portal.getCleanNetwork(); //Use a special check for bungee portals @@ -376,7 +406,8 @@ public final class PermissionHelper { * @param event

The move event causing the teleportation

* @return

True if the player cannot teleport. False otherwise

*/ - public static boolean playerCannotTeleport(Portal entrancePortal, Portal destination, Player player, PlayerMoveEvent event) { + public static boolean playerCannotTeleport(@Nullable Portal entrancePortal, @Nullable Portal destination, + @NotNull Player player, @Nullable PlayerMoveEvent event) { //No portal or not open if (entrancePortal == null || !entrancePortal.isOpen()) { return true; @@ -384,24 +415,27 @@ public final class PermissionHelper { //Not open for this player if (!entrancePortal.getPortalOpener().isOpenFor(player)) { - if (!entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); + if (!entrancePortal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.ACCESS_DENIED).error(player); } new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event); return true; } //No destination - if (!entrancePortal.getOptions().isBungee() && destination == null) { + boolean isBungee = entrancePortal.getOptions().isBungee(); + if (!isBungee && destination == null) { return true; } //Player cannot access portal if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destination)) { - if (!entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); + if (!entrancePortal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.ACCESS_DENIED).error(player); } new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event); + Stargate.debug("PermissionHelper::playerCannotTeleport", "Closed portal because player is " + + "missing necessary permissions"); entrancePortal.getPortalOpener().closePortal(false); return true; } diff --git a/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java b/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java index 74741fc..4e4f14b 100644 --- a/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/PortalFileHelper.java @@ -3,6 +3,7 @@ package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.BlockLocation; +import net.knarcraft.stargate.container.ControlBlockUpdateRequest; import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalHandler; @@ -10,6 +11,7 @@ import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.property.PortalLocation; import net.knarcraft.stargate.portal.property.PortalOptions; import net.knarcraft.stargate.portal.property.PortalOwner; +import net.knarcraft.stargate.portal.property.PortalStrings; import net.knarcraft.stargate.portal.property.gate.Gate; import net.knarcraft.stargate.portal.property.gate.GateHandler; import org.bukkit.Bukkit; @@ -20,11 +22,14 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; import org.bukkit.block.data.Waterlogged; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.List; import java.util.Scanner; import static net.knarcraft.stargate.portal.PortalSignDrawer.markPortalWithInvalidGate; @@ -43,7 +48,7 @@ public final class PortalFileHelper { * * @param world

The world to save portals for

*/ - public static void saveAllPortals(World world) { + public static void saveAllPortals(@NotNull World world) { Stargate.getStargateConfig().addManagedWorld(world.getName()); String saveFileLocation = Stargate.getPortalFolder() + "/" + world.getName() + ".db"; @@ -52,17 +57,22 @@ public final class PortalFileHelper { for (Portal portal : PortalRegistry.getAllPortals()) { //Skip portals in other worlds - String worldName = portal.getWorld().getName(); - if (!worldName.equalsIgnoreCase(world.getName())) { - continue; + if (portal.getWorld() == null) { + Stargate.logSevere(String.format("Could not save portal %s because its world is null", + portal.getName())); + } else { + String worldName = portal.getWorld().getName(); + if (!worldName.equalsIgnoreCase(world.getName())) { + continue; + } + //Save the portal + savePortal(bufferedWriter, portal); } - //Save the portal - savePortal(bufferedWriter, portal); } bufferedWriter.close(); - } catch (Exception e) { - Stargate.logSevere(String.format("Exception while writing stargates to %s: %s", saveFileLocation, e)); + } catch (Exception exception) { + Stargate.logSevere(String.format("Exception while writing stargates to %s: %s", saveFileLocation, exception)); } } @@ -73,13 +83,13 @@ public final class PortalFileHelper { * @param portal

The portal to save

* @throws IOException

If unable to write to the buffered writer

*/ - private static void savePortal(BufferedWriter bufferedWriter, Portal portal) throws IOException { + private static void savePortal(@NotNull BufferedWriter bufferedWriter, @NotNull Portal portal) throws IOException { StringBuilder builder = new StringBuilder(); BlockLocation button = portal.getStructure().getButton(); //WARNING: Because of the primitive save format, any change in order will break everything! builder.append(portal.getName()).append(':'); - builder.append(portal.getSignLocation().toString()).append(':'); + builder.append(portal.getSignLocation()).append(':'); builder.append((button != null) ? button.toString() : "").append(':'); //Add removes config values to keep indices consistent @@ -87,7 +97,7 @@ public final class PortalFileHelper { builder.append(0).append(':'); builder.append(portal.getYaw()).append(':'); - builder.append(portal.getTopLeft().toString()).append(':'); + builder.append(portal.getTopLeft()).append(':'); builder.append(portal.getGate().getFilename()).append(':'); //Only save the destination name if the gate is fixed as it doesn't matter otherwise @@ -111,20 +121,24 @@ public final class PortalFileHelper { * @param portal

The portal to save

* @param builder

The string builder to append to

*/ - private static void savePortalOptions(Portal portal, StringBuilder builder) { + private static void savePortalOptions(@NotNull Portal portal, @NotNull StringBuilder builder) { PortalOptions options = portal.getOptions(); builder.append(':'); builder.append(options.isHidden()).append(':'); builder.append(options.isAlwaysOn()).append(':'); builder.append(options.isPrivate()).append(':'); - builder.append(portal.getWorld().getName()).append(':'); + if (portal.getWorld() != null) { + builder.append(portal.getWorld().getName()).append(':'); + } else { + builder.append(':'); + } builder.append(options.isFree()).append(':'); builder.append(options.isBackwards()).append(':'); builder.append(options.isShown()).append(':'); builder.append(options.isNoNetwork()).append(':'); builder.append(options.isRandom()).append(':'); builder.append(options.isBungee()).append(':'); - builder.append(options.isSilent()).append(':'); + builder.append(options.isQuiet()).append(':'); builder.append(options.hasNoSign()); } @@ -134,7 +148,7 @@ public final class PortalFileHelper { * @param world

The world to load portals for

* @return

True if portals could be loaded

*/ - public static boolean loadAllPortals(World world) { + public static boolean loadAllPortals(@NotNull World world) { String location = Stargate.getPortalFolder(); File database = new File(location, world.getName() + ".db"); @@ -154,7 +168,7 @@ public final class PortalFileHelper { * @param database

The database file containing the portals

* @return

True if the portals were loaded successfully

*/ - private static boolean loadPortals(World world, File database) { + private static boolean loadPortals(@NotNull World world, @NotNull File database) { int lineIndex = 0; try { Scanner scanner = new Scanner(database); @@ -170,10 +184,9 @@ public final class PortalFileHelper { "Starting post loading tasks", world)); doPostLoadTasks(world, needsToSaveDatabase); return true; - } catch (Exception e) { - Stargate.logSevere(String.format("Exception while reading stargates from %s: %d", database.getName(), - lineIndex)); - e.printStackTrace(); + } catch (Exception exception) { + Stargate.logSevere(String.format("Exception while reading stargates from %s: %d! Message: %s", + database.getName(), lineIndex, exception.getMessage())); } return false; } @@ -186,7 +199,7 @@ public final class PortalFileHelper { * @param world

The world for which portals are currently being read

* @return

True if the read portal has changed and the world's database needs to be saved

*/ - private static boolean readPortalLine(Scanner scanner, int lineIndex, World world) { + private static boolean readPortalLine(@NotNull Scanner scanner, int lineIndex, @NotNull World world) { String line = scanner.nextLine().trim(); //Ignore empty and comment lines @@ -214,7 +227,7 @@ public final class PortalFileHelper { * @param world

The world portals have been loaded for

* @param needsToSaveDatabase

Whether the portal database's file needs to be updated

*/ - private static void doPostLoadTasks(World world, boolean needsToSaveDatabase) { + private static void doPostLoadTasks(@NotNull World world, boolean needsToSaveDatabase) { //Open any always-on portals. Do this here as it should be more efficient than in the loop. PortalHandler.verifyAllPortals(); int portalCount = PortalRegistry.getAllPortals().size(); @@ -224,11 +237,16 @@ public final class PortalFileHelper { Stargate.logInfo(String.format("{%s} Loaded %d stargates with %d set as always-on", world.getName(), portalCount, openCount)); + //Re-draw the signs in case a bug in the config prevented the portal from loading and has been fixed since + Stargate.debug("PortalFileHelper::doPostLoadTasks::update", + String.format("Queueing portal sign/button updates for %s", world)); for (Portal portal : PortalRegistry.getAllPortals()) { - if (portal.isRegistered()) { - portal.drawSign(); - updatePortalButton(portal); + if (portal.isRegistered() && portal.getWorld() != null && portal.getWorld().equals(world) && + world.getWorldBorder().isInside(portal.getSignLocation())) { + Stargate.addControlBlockUpdateRequest(new ControlBlockUpdateRequest(portal)); + Stargate.debug("UpdateSignsButtons", String.format("Queued sign and button updates for portal %s", + portal.getName())); } } //Save the portals to disk to update with any changes @@ -246,10 +264,10 @@ public final class PortalFileHelper { * @param lineIndex

The line index to report in case the user needs to fix an error

* @return

True if the portal's data has changed and its database needs to be updated

*/ - private static boolean loadPortal(String[] portalData, World world, int lineIndex) { + private static boolean loadPortal(@NotNull String[] portalData, @NotNull World world, int lineIndex) { //Load min. required portal data String name = portalData[0]; - BlockLocation button = (portalData[2].length() > 0) ? new BlockLocation(world, portalData[2]) : null; + BlockLocation button = (!portalData[2].isEmpty()) ? new BlockLocation(world, portalData[2]) : null; //Load the portal's location PortalLocation portalLocation = new PortalLocation(); @@ -275,7 +293,8 @@ public final class PortalFileHelper { PortalOwner owner = new PortalOwner(ownerString); //Create the new portal - Portal portal = new Portal(portalLocation, button, destination, name, network, gate, owner, + PortalStrings portalStrings = new PortalStrings(name, network, destination); + Portal portal = new Portal(portalLocation, button, portalStrings, gate, owner, PortalHandler.getPortalOptions(portalData)); //Register the portal, and close it in case it wasn't properly closed when the server stopped @@ -285,27 +304,6 @@ public final class PortalFileHelper { return buttonLocationChanged; } - /** - * Updates a portal's button if it does not match the correct material - * - * @param portal

The portal update the button of

- */ - private static void updatePortalButton(Portal portal) { - BlockLocation buttonLocation = getButtonLocation(portal); - if (portal.getOptions().isAlwaysOn()) { - //Clear button if not already air or water - if (MaterialHelper.isButtonCompatible(buttonLocation.getType())) { - Material newMaterial = decideRemovalMaterial(buttonLocation, portal); - Stargate.addBlockChangeRequest(new BlockChangeRequest(buttonLocation, newMaterial, null)); - } - } else { - //Replace button if the material does not match - if (buttonLocation.getType() != portal.getGate().getPortalButton()) { - generatePortalButton(portal, DirectionHelper.getBlockFaceFromYaw(portal.getYaw())); - } - } - } - /** * Decides the material to use for removing a portal's button/sign * @@ -313,7 +311,8 @@ public final class PortalFileHelper { * @param portal

The portal the button/sign belongs to

* @return

The material to use for removing the button/sign

*/ - public static Material decideRemovalMaterial(BlockLocation location, Portal portal) { + @NotNull + public static Material decideRemovalMaterial(@NotNull BlockLocation location, @NotNull Portal portal) { //Get the blocks to each side of the location Location leftLocation = location.getRelativeLocation(-1, 0, 0, portal.getYaw()); Location rightLocation = location.getRelativeLocation(1, 0, 0, portal.getYaw()); @@ -334,7 +333,7 @@ public final class PortalFileHelper { * @param location

The location to check

* @return

True if the location is underwater

*/ - private static boolean isUnderwater(Location location) { + private static boolean isUnderwater(@NotNull Location location) { BlockData blockData = location.getBlock().getBlockData(); return blockData.getMaterial() == Material.WATER || (blockData instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()); @@ -349,7 +348,7 @@ public final class PortalFileHelper { * @param portal

The portal to update the button vector for

* @return

True if the calculated button location is not the same as the one in the portal file

*/ - private static boolean updateButtonVector(Portal portal) { + private static boolean updateButtonVector(@NotNull Portal portal) { for (RelativeBlockVector control : portal.getGate().getLayout().getControls()) { BlockLocation controlLocation = portal.getLocation().getTopLeft().getRelativeLocation(control, portal.getYaw()); @@ -360,7 +359,7 @@ public final class PortalFileHelper { BlockLocation oldButtonLocation = portal.getStructure().getButton(); if (oldButtonLocation != null && !oldButtonLocation.equals(buttonLocation)) { - Stargate.addBlockChangeRequest(new BlockChangeRequest(oldButtonLocation, Material.AIR, null)); + Stargate.addControlBlockUpdateRequest(new BlockChangeRequest(oldButtonLocation, Material.AIR, null)); portal.getStructure().setButton(buttonLocation); return true; } @@ -375,13 +374,24 @@ public final class PortalFileHelper { * @param portal

The portal to generate button for

* @param buttonFacing

The direction the button should be facing

*/ - public static void generatePortalButton(Portal portal, BlockFace buttonFacing) { + public static void generatePortalButton(@NotNull Portal portal, @NotNull BlockFace buttonFacing) { //Go one block outwards to find the button's location rather than the control block's location BlockLocation button = getButtonLocation(portal); - Directional buttonData = (Directional) Bukkit.createBlockData(portal.getGate().getPortalButton()); - buttonData.setFacing(buttonFacing); - button.getBlock().setBlockData(buttonData); + // If the button location is null here, it is assumed that the button generation wasn't necessary + if (button == null) { + return; + } + + if (!MaterialHelper.isButtonCompatible(button.getType())) { + @NotNull List possibleMaterials = MaterialHelper.specifiersToMaterials( + portal.getGate().getPortalButtonMaterials()).stream().toList(); + Material buttonType = ListHelper.getRandom(possibleMaterials); + + Directional buttonData = (Directional) Bukkit.createBlockData(buttonType); + buttonData.setFacing(buttonFacing); + button.getBlock().setBlockData(buttonData); + } portal.getStructure().setButton(button); } @@ -391,11 +401,16 @@ public final class PortalFileHelper { * @param portal

The portal to find the button for

* @return

The location of the portal's button

*/ - private static BlockLocation getButtonLocation(Portal portal) { + @Nullable + public static BlockLocation getButtonLocation(@NotNull Portal portal) { BlockLocation topLeft = portal.getTopLeft(); RelativeBlockVector buttonVector = portal.getLocation().getButtonVector(); - return topLeft.getRelativeLocation(buttonVector.addToVector(RelativeBlockVector.Property.OUT, 1), - portal.getYaw()); + + if (buttonVector == null) { + return null; + } + + return topLeft.getRelativeLocation(buttonVector.addOut(1), portal.getYaw()); } } diff --git a/src/main/java/net/knarcraft/stargate/utility/SignHelper.java b/src/main/java/net/knarcraft/stargate/utility/SignHelper.java new file mode 100644 index 0000000..f76acc9 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/utility/SignHelper.java @@ -0,0 +1,51 @@ +package net.knarcraft.stargate.utility; + +import org.bukkit.DyeColor; +import org.bukkit.block.Sign; +import org.bukkit.block.sign.Side; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A helper class for dealing with signs + */ +public final class SignHelper { + + private SignHelper() { + + } + + /** + * Gets the lines of the given sign + * + * @param sign

The sign to get lines from

+ * @return

The lines of the sign

+ */ + @NotNull + public static String[] getLines(@NotNull Sign sign) { + return sign.getSide(Side.FRONT).getLines(); + } + + /** + * Gets the dye color of the given sign + * + * @param sign

The sign to check

+ * @return

The dye currently applied to the sign

+ */ + @Nullable + public static DyeColor getDye(@NotNull Sign sign) { + return sign.getSide(Side.FRONT).getColor(); + } + + /** + * Sets the text of a line on a sign + * + * @param sign

The sign to set text for

+ * @param line

The line to set

+ * @param text

The text to set

+ */ + public static void setSignLine(@NotNull Sign sign, int line, @NotNull String text) { + sign.getSide(Side.FRONT).setLine(line, text); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/utility/TeleportHelper.java b/src/main/java/net/knarcraft/stargate/utility/TeleportHelper.java index 97d7868..0ebf770 100644 --- a/src/main/java/net/knarcraft/stargate/utility/TeleportHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/TeleportHelper.java @@ -1,6 +1,8 @@ package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.Message; +import net.knarcraft.stargate.config.SGFormatBuilder; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.teleporter.EntityTeleporter; import org.bukkit.Bukkit; @@ -10,6 +12,7 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -31,7 +34,7 @@ public final class TeleportHelper { * @param player

The player trying to teleport

* @return

False if the player has leashed any creatures that cannot go through the portal

*/ - public static boolean noLeashedCreaturesPreventTeleportation(Player player) { + public static boolean noLeashedCreaturesPreventTeleportation(@NotNull Player player) { //Find any nearby leashed entities to teleport with the player List nearbyCreatures = getLeashedCreatures(player); @@ -41,7 +44,6 @@ public final class TeleportHelper { return false; } } - //TODO: Improve this to account for any players sitting on any of the lead creatures //If it's enabled, there is no problem if (Stargate.getGateConfig().handleLeashedCreatures()) { @@ -57,7 +59,8 @@ public final class TeleportHelper { * @param player

The player to check

* @return

A list of all creatures the player is holding in a leash (lead)

*/ - public static List getLeashedCreatures(Player player) { + @NotNull + public static List getLeashedCreatures(@NotNull Player player) { List leashedCreatures = new ArrayList<>(); //Find any nearby leashed entities to teleport with the player List nearbyEntities = player.getNearbyEntities(15, 15, 15); @@ -81,8 +84,8 @@ public final class TeleportHelper { * @param exitDirection

The direction of any passengers exiting the stargate

* @param newVelocity

The new velocity of the teleported passenger

*/ - public static void teleportAndAddPassenger(Entity targetVehicle, Entity passenger, Vector exitDirection, - Vector newVelocity) { + public static void teleportAndAddPassenger(@NotNull Entity targetVehicle, @NotNull Entity passenger, + @NotNull Vector exitDirection, @NotNull Vector newVelocity) { Location passengerExit = targetVehicle.getLocation().clone().setDirection(exitDirection); if (!passenger.teleport(passengerExit)) { Stargate.debug("TeleportHelper::handleVehiclePassengers", "Failed to teleport passenger" + @@ -113,8 +116,9 @@ public final class TeleportHelper { * @param exitRotation

The rotation of any passengers exiting the stargate

* @param newVelocity

The new velocity of the teleported passengers

*/ - public static void handleEntityPassengers(List passengers, Entity entity, Portal origin, Portal target, - Vector exitRotation, Vector newVelocity) { + public static void handleEntityPassengers(@NotNull List passengers, @NotNull Entity entity, + @NotNull Portal origin, @NotNull Portal target, + @NotNull Vector exitRotation, @NotNull Vector newVelocity) { for (Entity passenger : passengers) { List passengerPassengers = passenger.getPassengers(); if (!passengerPassengers.isEmpty()) { @@ -145,7 +149,7 @@ public final class TeleportHelper { * @param origin

The portal the player is teleporting from

* @param target

The portal the player is teleporting to

*/ - public static void teleportLeashedCreatures(Player player, Portal origin, Portal target) { + public static void teleportLeashedCreatures(@NotNull Player player, @NotNull Portal origin, @NotNull Portal target) { //If this feature is disabled, just return if (!Stargate.getGateConfig().handleLeashedCreatures()) { return; @@ -172,7 +176,7 @@ public final class TeleportHelper { * @param entities

The list of entities to check

* @return

True if at least one entity is not a player

*/ - public static boolean containsNonPlayer(List entities) { + public static boolean containsNonPlayer(@NotNull List entities) { for (Entity entity : entities) { if (!(entity instanceof Player) || containsNonPlayer(entity.getPassengers())) { return true; @@ -187,7 +191,7 @@ public final class TeleportHelper { * @param entities

The list of entities to check

* @return

True if at least one player is present among the passengers

*/ - public static boolean containsPlayer(List entities) { + public static boolean containsPlayer(@NotNull List entities) { for (Entity entity : entities) { if (entity instanceof Player || containsPlayer(entity.getPassengers())) { return true; @@ -202,7 +206,8 @@ public final class TeleportHelper { * @param entities

The entities to check for players

* @return

The found players

*/ - public static List getPlayers(List entities) { + @NotNull + public static List getPlayers(@NotNull List entities) { List players = new ArrayList<>(5); for (Entity entity : entities) { if (entity instanceof Player) { @@ -221,11 +226,12 @@ public final class TeleportHelper { * @param destinationPortal

The portal the player is to exit from

* @return

True if the player is allowed to teleport and is able to pay necessary fees

*/ - public static boolean playerCanTeleport(Player player, Portal entrancePortal, Portal destinationPortal) { + public static boolean playerCanTeleport(@NotNull Player player, @NotNull Portal entrancePortal, + @NotNull Portal destinationPortal) { //Make sure the user can access the portal if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destinationPortal)) { - if (!entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); + if (!entrancePortal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.ACCESS_DENIED).error(player); } entrancePortal.getPortalOpener().closePortal(false); return false; @@ -235,8 +241,8 @@ public final class TeleportHelper { int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal); boolean canAffordFee = cost <= 0 || Stargate.getEconomyConfig().canAffordFee(player, cost); if (!canAffordFee) { - if (!entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("ecoInFunds")); + if (!entrancePortal.getOptions().isQuiet()) { + new SGFormatBuilder(Message.ECONOMY_INSUFFICIENT).error(player); } return false; } diff --git a/src/main/java/net/knarcraft/stargate/utility/UUIDMigrationHelper.java b/src/main/java/net/knarcraft/stargate/utility/UUIDMigrationHelper.java index 4cb4472..0e93892 100644 --- a/src/main/java/net/knarcraft/stargate/utility/UUIDMigrationHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/UUIDMigrationHelper.java @@ -7,6 +7,7 @@ import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.property.PortalOwner; import org.bukkit.OfflinePlayer; import org.bukkit.World; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.HashMap; @@ -35,7 +36,7 @@ public final class UUIDMigrationHelper { * * @param player

The player to migrate

*/ - public static void migrateUUID(OfflinePlayer player) { + public static void migrateUUID(@NotNull OfflinePlayer player) { Map> playersToMigrate = getPlayersToMigrate(); String playerName = player.getName(); @@ -53,7 +54,7 @@ public final class UUIDMigrationHelper { migratePortalsToUUID(portalsOwned, player.getUniqueId()); - //Remove the player to prevent the migration to happen every time the player joins + //Remove the player to prevent the migration from happening every time the player joins playersToMigrate.remove(playerName); } @@ -63,7 +64,7 @@ public final class UUIDMigrationHelper { * @param portals

The portals to migrate

* @param uniqueId

The unique ID of the portals' owner

*/ - private static void migratePortalsToUUID(List portals, UUID uniqueId) { + private static void migratePortalsToUUID(@NotNull List portals, @NotNull UUID uniqueId) { Set worldsToSave = new HashSet<>(); //Get the real portal from the copy and set UUID @@ -86,6 +87,7 @@ public final class UUIDMigrationHelper { * * @return

The player names to migrate

*/ + @NotNull private static Map> getPlayersToMigrate() { //Make sure to only go through portals once if (playerNamesToMigrate != null) { diff --git a/src/main/resources/config-migrations.txt b/src/main/resources/config-migrations.txt index cb2628f..43893cd 100644 --- a/src/main/resources/config-migrations.txt +++ b/src/main/resources/config-migrations.txt @@ -1,7 +1,8 @@ lang=language -defaultNetwork=defaultGateNetwork +defaultNetwork=gates.defaultGateNetwork use-mysql= ignoreEntrance= +enableEconomy=economy.useEconomy portal-save-location=folders.portalFolder portal-folder=folders.portalFolder gate-folder=folders.gateFolder @@ -16,7 +17,7 @@ sortLists=gates.cosmetic.sortNetworkDestinations protectEntrance=gates.integrity.protectEntrance enableBungee=gates.functionality.enableBungee verifyPortals=gates.integrity.verifyPortals -signColor=gates.cosmetic.signColor +signColor=gates.cosmetic.mainSignColor gates.cosmetic.signColor=gates.cosmetic.mainSignColor debug=debugging.debug permdebug=debugging.permissionDebug @@ -45,5 +46,6 @@ portal-open= portal-closed= cost-type= cost-to-activate= -taxaccount=taxAccount +taxAccount=economy.taxAccount +taxaccount=economy.taxAccount usevault= \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b8d6dbc..78884bd 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,31 +1,137 @@ -# stargate Configuration File -# Main stargate config +# Version: ${project.version} +# +--------------▄▄▄-- ‚——. -▄--▄-▄--▄-▄-▄▄▄▄▄---------------------------------------------------+ # +# | █▄▄▀ (“‡‡”) █▄▄▀ █▄▄▀ █ █ Support available at: sgrewritten.org/discord | # +# | █▄▄█ \__/ █ █ █ █ █ █ ‚-. | # +# | —————————————————————————————————————————————————— ((_)) —————————————————— | # +# | ‘-’ | # +# | .|'''.| |''||''| | '||''|. ..|'''.| | |''||''| '||''''| | # +# | ||.. ' || ||| || || .|' ' ||| || || . | # +# | ''|||. || | || ||''|' || .... | || || ||''| | # +# | . '|| || .''''|. || |. '|. || ;’ |. || || | # +# | '....|' .||. .|. .||. .||. '|' ''|...'| .|. .||. .||. .||.....| | # +# +----------------------------------------------------------------------------------------------+ # +# | UNIFIED LEGACY BRANCH | # +# +----------------------------------------------+-----------------------------------------------+ # +# | Documentation: sgrewritten.org/legacywiki | Bug Reports: sgrewritten.org/report | # +# +----------------------------------------------+-----------------------------------------------+ # -# language - The language file to load for messages (de,en,es,fr,hu,it,ja,nb-no,nl,nn-no,pt-br,ru,zh_cn) +# +----------------------------------------------------------------------------------------------+ # +# | General Preferences | # +# +----------------------------------------------------------------------------------------------+ # + +# What language will stargate use when communicating with your users? +# Supported values: [cs, de, en, es, fr, hu, it, ja, nb, nl, nn, pt, ru, sv, tr, uk, zh] +# +# If SG isn't yet available in your language, please consider adding a translation: +# https://sgrewritten.org/translate +# For more information on language codes, see ISO 639-1: https://git.io/JcwaI language: en -# adminUpdateAlert - Whether to alert admins about new plugin updates + +# Would you like to us to notify admin users when new StarGate updates are available? adminUpdateAlert: true -folders: - # portalFolder - The folder for storing portals - portalFolder: plugins/Stargate/portals/ - # gateFolder - The folder for storing gate layouts - gateFolder: plugins/Stargate/gates/ + +# +----------------------------------------------------------------------------------------------+ # +# | Compatibility | # +# +----------------------------------------------------------------------------------------------+ # +dynmap: + # Should StarGate enable integration with Dynmap? This will show StarGates on your map. + enableDynmap: true + + # Should StarGate icons be hidden by default? (must users manually enable their checkbox?) + dynmapIconsHiddenByDefault: true + +# +----------------------------------------------------------------------------------------------+ # +# | Gate Behaviour | # +# +----------------------------------------------------------------------------------------------+ # gates: - # maxGatesEachNetwork - The maximum number of gates allowed on a network - 0 for unlimited + # What is the maximum number of gates a single network may contain? (0 for unlimited) maxGatesEachNetwork: 0 - # defaultGateNetwork - The default gate network + + # What network will be used when none has been specified? (Max length 12 characters) + # + # Note that this primarily applies to users with the stargate.create.network node. + # Generally, that node is given to staff (we recommend stargate.create.personal for users). defaultGateNetwork: central - # exitVelocity - The velocity to give players exiting stargates, relative to the entry velocity + + # At what speed should players be sent out of portals? + # When exiting a gate, players retain their original movement velocity, multiplied by this value. exitVelocity: 0.1 + + functionality: + # Should an alternative (and less effective) teleportation script be used to teleport vehicles? + # This will fix an incompatibility with CraftBook, but will cause vehicles' data to be wiped on teleportation. + enableCraftBookRemoveOnEjectFix: false + + # Are you connected to a BungeeCord-compatible proxy? + # Set this value to true if you intend on building gates with the bUngee flag. + enableBungee: false + + # Will vehicles and their passengers be able to travel through StarGate portals? + # [minecarts, boats, & saddled mobs = vehicles | players & mobs = passengers] + handleVehicles: true + + # Should vehicles without any passengers be allowed to go through a StarGate? + handleEmptyVehicles: true + + # Should StarGate teleport creatures through portals? + # For example, if a player and a mob are in a boat, should the teleportation be allowed? + handleCreatureTransportation: true + + # If handleCreatureTransportation is true, must the player be present? + # I.e., can a mob in a vehicle go through a gate on its own? + handleNonPlayerVehicles: true + + # Should StarGate also handle creatures attached to a player via a lead? + handleLeashedCreatures: true + + integrity: + + # Can StarGates be broken via an explosion? + # [tnt, creepers, etc.] + destroyedByExplosion: false + + # Will the server re-verify all existing portals on startup? + # + # This checks that the expected gates are both present and using a valid layout. + # Designed to catch .gate file modifications, world changes, and terrain regeneration. + # + # …««»»… If you set this to true, you should also set protectEntrance (below) to true! + # “NOTE” Otherwise, disallowed players, block updates, and snowmen can invalidate your portals! + verifyPortals: false + + # Should the portal verification process account for iris (open/closed) materials? + # i.e. will a gate still validate if its portal-open material isn't present? + # + # This is more resource intensive, and should really only be used if verifyPortals is true. + # Or if using an easily destroyable open/closed material. + protectEntrance: false + + # How many ticks should go between each Stargate control update? This process updates any signs that have incorrect + # information, and buttons that are missing. While a value of one works fine for small servers with few Stargates, + # it has been known to cause lag and high initial RAM usage for huge servers. A value of 20 is one second, which + # should work no matter how many Stargates the server has. + controlUpdateDelay: 3 + + # +----------------------------------------------------------------------------------------------+ # + # | Aesthetic Tweaks | # + # +----------------------------------------------------------------------------------------------+ # cosmetic: - # rememberDestination - Whether to remember the cursor location between uses + # Will the destination a networked portal last connected to be listed first in its scroll menu? rememberDestination: false - # sortNetworkDestinations - Whether to sort network lists alphabetically + + # For networked gates, are destinations listed alphabetically instead of chronologically? + # (This applies to all non-fixed and non-random gates). sortNetworkDestinations: false - # mainSignColor - The color used for drawing signs (Default: BLACK). + + # What color will StarGate use for the text on gate signs? + # Note that players can override this with DYE and/or GLOW_INK_SAC mainSignColor: BLACK - # highlightSignColor - The color used for sign markings (Default: WHITE) + + # What color will StarGate use to accent the above text? highlightSignColor: WHITE + + # Text and highlight colors can be modified on a per-sign basis (below). + # Format: 'SIGN_TYPE:mainSignColor,highlightSignColor' perSignColors: - 'ACACIA:default,default' - 'BIRCH:default,default' @@ -35,73 +141,90 @@ gates: - 'OAK:default,default' - 'SPRUCE:inverted,inverted' - 'WARPED:inverted,inverted' - integrity: - # destroyedByExplosion - Whether to destroy gates with explosions (Creeper, TNT, etc.) - destroyedByExplosion: false - # verifyPortals - Whether all the non-sign blocks are checked to match the gate layout when a stargate is loaded. - verifyPortals: false - # protectEntrance - Whether to protect gate entrance material (More resource intensive. Only enable if using - # destroyable open/closed material) - protectEntrance: false - functionality: - enableBungee: false - # handleVehicles - Whether to allow vehicles through gates. This overrides other vehicle settings - handleVehicles: true - # handleEmptyVehicles - Whether to allow empty vehicles through gates (chest/hopper/tnt/furnace minecarts included) - handleEmptyVehicles: true - # handleCreatureTransportation - Whether to allow players to transport creatures by sending vehicles (minecarts, - # boats) through gates - handleCreatureTransportation: true - # handleNonPlayerVehicles - Whether to allow vehicles with a passenger which is not a player through gates. - # handleCreatureTransportation must be enabled - handleNonPlayerVehicles: true - # handleLeashedCreatures - Whether to allow creatures lead by a player to teleport with the player - handleLeashedCreatures: true - # enableCraftBookRemoveOnEjectFix - Whether to enable a fix that causes loss of NBT data, but allows vehicle - # teleportation to work when CraftBook's remove minecart/boat on eject setting is enabled - enableCraftBookRemoveOnEjectFix: false + - 'BAMBOO:default,default' + - 'CHERRY:default,default' + - 'PALE_OAK:default,default' + - 'MANGROVE:default,default' -# ######################## # -# stargate economy options # -# ######################## # economy: - # useEconomy - Whether to use an economy plugin - useEconomy: false - # createCost - The cost to create a gate - createCost: 0 - # destroyCost - The cost to destroy a gate - destroyCost: 0 - # useCost - The cost to use a gate - useCost: 0 - # toOwner - Whether the charge for using a gate goes to the gate's owner - toOwner: false - # chargeFreeDestination - Whether a gate whose destination is a free gate is still charged - chargeFreeDestination: true - # freeGatesColored - Whether a free gate in the destination list is marked with a color + # When scrolling through a networked portal's destination list, should SG color free gates? freeGatesColored: false - # freeGatesColor - The color to use for marking free gates + + # If freeGatesColored is true, which color should SG use? + # + # All color names should be a hex RGB color (#ffffff) or follow this format: + # https://hub.spigotmc.org/javadocs/spigot/org/bukkit/ChatColor.html freeGatesColor: DARK_GREEN + + # +----------------------------------------------------------------------------------------------+ # + # | Economy | # + # +----------------------------------------------------------------------------------------------+ # + + # Will StarGate interact with your server's economy? + # + # …««»»… For these features, StarGate depends on the Vault plugin. + # “NOTE” https://www.spigotmc.org/resources/vault.34315/ + useEconomy: false + + # How much will be deducted from players' accounts when creating a new gate? + createCost: 0 + + # How much will be deducted from players' accounts when destroying an existing gate? + destroyCost: 0 + + # How much will be deducted from players' accounts when using a stargate to teleport? + # This does not apply to stargates with the Free flag. + useCost: 0 + + # Will fees collected for ‘usecost’ be paid to whomever created (owns) the applicable stargate? + # If false, collected funds will be deleted. + toOwner: false + + # Will ‘createcost’ still be charged if the new gate's destination is a gate with the Free flag? + # Note that this only applies to fixed gates. + chargeFreeDestination: true + + # Does your server have a tax account (closed economy)? + # If so, please provide the name of your tax account (collected money will be sent to it). + # If not, leave this section blank (collected money will be deleted). + # + # Note that useCost money is excluded from this system when toOwner is true. + taxAccount: '' -# ############# # -# Debug options # -# ############# # +# +----------------------------------------------------------------------------------------------+ # +# | Technical | # +# +----------------------------------------------------------------------------------------------+ # + +# | [ Storage ] | # +# |__ These settings are provided to customise how SG stores its data. __| # +folders: + # Currently, all valid gates, their details, and their owners, are stored in a flatfile database + # Where should that flatfile be stored? + # + # This option is provided as a patch to allow data to be imported from some older forks. + portalFolder: plugins/Stargate/portals/ + + # Currently, on startup, a folder is checked for gate layouts stored as ‘.gate’ files. + # Where is that folder? + # + # This option is provided as a patch to allow data to be imported from some older forks. + gateFolder: plugins/Stargate/gates/ + +# | [ Debug ] | # +# |__ These settings are provided to help developers diagnose issues with this plugin. __| # debugging: - # debug - Debug -- Only enable if you have issues, massive console output + # Should SG spam console with A LOT of technical information? + # This includes checks, events, etc. debug: false - # permissionDebug - This will output any and all Permissions checks to console, used for permissions debugging - # (Requires debug: true) + + # Should SG spam console with A LOT of permission-based information? + # This visualises how stargate determines what players can do. + # + # Note that (regardless of this setting), permdebug is always hidden when ‘debug’ is false. permissionDebug: false -advanced: - # waitForPlayerAfterTeleportDelay - The amount of ticks to wait before adding a player as passenger of a vehicle. - # On slow servers, a value of 6 is required to avoid client glitches after teleporting on a vehicle. - waitForPlayerAfterTeleportDelay: 6 -# ############## # -# Dynmap options # -# ############## # -dynmap: - # enableDynmap - Whether to display Stargates in Dynmap's map - enableDynmap: true - # dynmapIconsHiddenByDefault - Whether to hide the set of Stargate icons by default, requiring users to - # manually enable them with a checkbox. - dynmapIconsHiddenByDefault: true \ No newline at end of file +advanced: + # How long should SG wait before returning players to their vehicles after teleportation? + # This is done to prevent possible visual bugs; slower servers need larger values. + # In most cases, a value of 6 should be fine. + waitForPlayerAfterTeleportDelay: 6 \ No newline at end of file diff --git a/src/main/resources/gates/wool.gate b/src/main/resources/gates/wool.gate new file mode 100644 index 0000000..c7d53e1 --- /dev/null +++ b/src/main/resources/gates/wool.gate @@ -0,0 +1,12 @@ +portal-open=WATER +portal-closed=AIR +button=STONE_BUTTON +toowner=false +X=#WOOL +-=#WOOL + +XXXXX +X...X +-...- +X.*.X +XXXXX \ No newline at end of file diff --git a/src/main/resources/lang/cs.txt b/src/main/resources/lang/cs.txt new file mode 100644 index 0000000..25e3aaf --- /dev/null +++ b/src/main/resources/lang/cs.txt @@ -0,0 +1,28 @@ +# Stargate Legacy Localisation File: Czech +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=Domavatus +bungeeDeny=Nemáte oprávnění vytvářet bungee brány! +bungeeDisabled=Podpora Bungeecord není povolena. +bungeeEmpty=Starší bungee brány vyžadují určení cílové brány a server. +createConflict=Tato brána je v konfliktu s existující branou. +createExists=Brána se stejným názvem již existuje. +createFull=Tato síť je plná! +createGateDeny=Nemáte přístup k tomuto schématu brány! +createNameLength=Název je příliš krátký nebo příliš dlouhý! +createNetDeny=Nemáte přístup k této síti! +createPersonal=Vytváření brány v osobní síti. +denyMsg=Přístup zamítnut! +destEmpty=Seznam cílů je prázdný! +destroyMsg=Brána zničena! +ecoDeduct=Odečteno %cost%. +ecoInFunds=Nedostatek prostředků! +ecoObtain=Získáno %cost% od Hvězdné brány %portal%.! +invalidMsg=Neplatný cíl! +prefix=[Stargate] +reloaded=Konfigurace obnovena! +signDisconnected=Odpojeno +signRandom=Náhodná +signRightClick=Pravý klik +signToUse=k použití brány +teleportMsg=Teleportován! \ No newline at end of file diff --git a/src/main/resources/lang/de.txt b/src/main/resources/lang/de.txt index c2c2f61..3e2ee06 100644 --- a/src/main/resources/lang/de.txt +++ b/src/main/resources/lang/de.txt @@ -1,28 +1,32 @@ -author=EduardBaer -prefix=[Stargate] -teleportMsg=Du wurdest Teleportiert. -destroyMsg=Gate zerstört -invalidMsg=Ungültiges Ziel +# Stargate Legacy Localisation File: German +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=EduardBaer & Matriarch! blockMsg=Ziel blockiert -destEmpty=Zielliste leer -denyMsg=Zugriff verweigert - -ecoDeduct=%cost% abgezogen -ecoRefund=%cost% zurückerstattet -ecoObtain=%cost% von Stargate %portal% erhalten -ecoInFunds=Das kannst du dir nicht leisten. - +bungeeDeny=Du hast keine Berechtigung, Bungee-Tore zu erstellen! +bungeeDisabled=Bungeecord-Unterstützung ist nicht aktiviert. +bungeeEmpty=Legacy-Bungee-Gates erfordern, dass Sie ein Zieltor und einen Server angeben. +createConflict=Dieses Tor steht im Widerspruch zu einem bestehenden Tor. +createExists=Es existiert schon ein Portal mit diesem Namen. +createFull=Dieses Netzwerk ist voll ! +createGateDeny=Du hast kein Zugang zu dieses Portal format! createMsg=Gate erstellt. -createNetDeny=Du hast keinen Zugriff auf dieses Netzwerk. -createGateDeny=Du hast keinen Zugriff auf dieses Gate-Layout. -createPersonal=Gate im persönlichen Netzwerk erstellt. -createNameLength=Name zu kurz oder zu lang. -createExists=Ein Gate mit diesem Name existiert bereits. -createFull=Dieses Netzwerk ist voll. +createNameLength=Name zu kurz oder zu lang! +createNetDeny=Du hast kein Zugang zu dieses Netzwerk! +createPersonal=Erstelle Tor im persönlichen Netzwerk. createWorldDeny=Du hast keinen Zugriff auf diese Welt. -createConflict=Dieses Gate steht im Konflikt mit einem bereits existierenden. - -signRightClick=Right click -signToUse=to use gate -signRandom=Random -signDisconnected=Disconnected \ No newline at end of file +denyMsg=Zugang verweigert! +destEmpty=Zielliste ist leer! +destroyMsg=Tor zerstört +ecoDeduct=%cost% abgezogen . +ecoInFunds=Unzureichende Mittel +ecoObtain=%cost% von StarGate %portal% erhalten! +ecoRefund=%cost% zurückerstattet +invalidMsg=Ungültiger Ziel! +prefix=[Stargate] +reloaded=Konfiguration wurde neu geladen! +signDisconnected=Verbindung getrennt +signRandom=Willkürlich +signRightClick=Rechtsklick +signToUse=um Tor zu benutzen +teleportMsg=Teleportiert! \ No newline at end of file diff --git a/src/main/resources/lang/en.txt b/src/main/resources/lang/en.txt index c27e099..6f00056 100644 --- a/src/main/resources/lang/en.txt +++ b/src/main/resources/lang/en.txt @@ -1,43 +1,41 @@ -prefix=[Stargate] -teleportMsg=Teleported -destroyMsg=Gate Destroyed -invalidMsg=Invalid Destination +# Stargate Legacy Localisation File: English +# For a more intuitive interface, please use https://translate.sgrewritten.org + blockMsg=Destination Blocked -destEmpty=Destination List Empty -denyMsg=Access Denied -reloaded=Stargate Reloaded - -ecoDeduct=Deducted %cost% -ecoRefund=Refunded %cost% -ecoObtain=Obtained %cost% from Stargate %portal% -ecoInFunds=Insufficient Funds -ecoLoadError=Vault was loaded, but no economy plugin could be hooked into -vaultLoadError=Economy is enabled but Vault could not be loaded. Economy disabled -vaultLoaded=Vault v%version% found - -createMsg=Gate Created -createNetDeny=You do not have access to that network -createGateDeny=You do not have access to that gate layout -createPersonal=Creating gate on personal network -createNameLength=Name too short or too long. -createExists=A gate by that name already exists -createFull=This network is full -createWorldDeny=You do not have access to that world -createConflict=Gate conflicts with existing gate - -signRightClick=Right click -signToUse=to use gate -signRandom=Random -signDisconnected=Disconnected -signInvalidGate=Invalid gate - -bungeeDisabled=BungeeCord support is disabled. bungeeDeny=You do not have permission to create BungeeCord gates. +bungeeDisabled=BungeeCord support is disabled. bungeeEmpty=BungeeCord gates require both a destination and network. bungeeSign=Teleport to - -portalInfoTitle=[STARGATE INFO] -portalInfoName=Name: %name% +createConflict=Gate conflicts with existing gate +createExists=A gate by that name already exists +createFull=This network is full +createGateDeny=You do not have access to that gate layout +createMsg=Gate Created +createNameLength=Name too short or too long. +createNetDeny=You do not have access to that network +createPersonal=Creating gate on personal network +createWorldDeny=You do not have access to that world +denyMsg=Access Denied +destEmpty=Destination List Empty +destroyMsg=Gate Destroyed +ecoDeduct=Deducted %cost% +ecoInFunds=Insufficient Funds +ecoLoadError=Vault was loaded, but no economy plugin could be hooked into +ecoObtain=Obtained %cost% from Stargate %portal% +ecoRefund=Refunded %cost% +invalidMsg=Invalid Destination portalInfoDestination=Destination: %destination% +portalInfoName=Name: %name% portalInfoNetwork=Network: %network% -portalInfoServer=Server: %server% \ No newline at end of file +portalInfoServer=Server: %server% +portalInfoTitle=[STARGATE INFO] +prefix=[Stargate] +reloaded=Stargate Reloaded +signDisconnected=Disconnected +signInvalidGate=Invalid gate +signRandom=Random +signRightClick=Right click +signToUse=to use gate +teleportMsg=Teleported +vaultLoadError=Economy is enabled but Vault could not be loaded. Economy disabled +vaultLoaded=Vault v%version% found \ No newline at end of file diff --git a/src/main/resources/lang/es.txt b/src/main/resources/lang/es.txt index 5c5338f..a229f55 100644 --- a/src/main/resources/lang/es.txt +++ b/src/main/resources/lang/es.txt @@ -1,28 +1,32 @@ -author=Manuestaire -prefix=[Stargate] -teleportMsg=Teletransportado -destroyMsg=Portal Destruído -invalidMsg=Elige Destino +# Stargate Legacy Localisation File: Spanish +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author= Manuestaire & Translated s.r.l. blockMsg=Destino Bloqueado -destEmpty=La lista de destinos está vacía -denyMsg=Acceso denegado - -ecoDeduct=Pagaste %cost% -ecoRefund=Reembolsado %cost% -ecoObtain=Obtenido %cost% del Portal %portal% -ecoInFunds=No tienes suficiente dinero - +bungeeDeny=¡No tienes permiso para hacer puertas bungee! +bungeeDisabled=El soporte de Bungeecord no está habilitado. +bungeeEmpty=Las puertas bungee antiguas requieren que especifiques una puerta de destino y un servidor. +createConflict=Esta puerta entra en conflicto con una puerta existente. +createExists=¡Ya existe una puerta con ese nombre! +createFull=¡Esa red está llena! +createGateDeny=¡No tienes acceso a ese diseño de puerta! createMsg=Portal creado -createNetDeny=No tienes acceso a esta red -createGateDeny=No tienes acceso a este diseño de portal -createPersonal=Creando el portal en una red personal -createNameLength=Nombre demasiado largo o demasiado corto -createExists=Ya existe una puerta con este nombre -createFull=Esta red está llena +createNameLength=¡Nombre demasiado corto o demasiado largo! +createNetDeny=¡No tienes acceso a esa red! +createPersonal=Creando puerta en la red personal. createWorldDeny=No tienes permisos para acceder a ese mundo -createConflict=El portal entra en conflicto con un portal ya existente - -signRightClick=Click derecho -signToUse=para usar +denyMsg=¡Acceso denegado! +destEmpty=¡Lista de destinos vacía! +destroyMsg=¡Puerta destruida! +ecoDeduct=Se han restado %cost%. +ecoInFunds=¡Fondos insuficientes! +ecoObtain=¡Has obtenido %cost% del portal %portal% de StarGate! +ecoRefund=Reembolsado %cost% +invalidMsg=¡Destino no válido! +prefix=[Stargate] +reloaded=¡Configuración cargada! +signDisconnected=Desconectado signRandom=Aleatorio -signDisconnected=Desconectado \ No newline at end of file +signRightClick=Clic derecho +signToUse=y usa la puerta +teleportMsg=¡Teletransportación completada! \ No newline at end of file diff --git a/src/main/resources/lang/fr.txt b/src/main/resources/lang/fr.txt index 66fd9ca..b434efc 100644 --- a/src/main/resources/lang/fr.txt +++ b/src/main/resources/lang/fr.txt @@ -1,28 +1,42 @@ -author=Dauphin14 -prefix=[Stargate] -teleportMsg=Téléportation Réussie. -destroyMsg=Portail detruit. -invalidMsg=Destination invalide. +# Stargate Legacy Localisation File: French +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=Damiendier blockMsg=Destination bloquée. -destEmpty=Aucune Destination disponible. -denyMsg=Accès Refusé. - -ecoDeduct=Retrait de %cost%. -ecoRefund=Remboursement de %cost%. -ecoObtain=Gain de %cost% du Portail %portal%. -ecoInFunds=Ressources insuffisantes. - -createMsg=Portail Créé -createNetDeny=Vous n'avez pas accès à ce réseau. -createGateDeny=Vous n'avez pas accès à ce type de portail. -createPersonal=Portail créé sur le réseau personnel. -createNameLength=Nom trop long ou trop court. -createExists=Il existe déjà un portail avec le même nom. -createFull=Le reseau est plein. -createWorldDeny=Vous n'avez pas accès à ce monde. -createConflict=Ce portail entre en conflit avec un portail déjà existant. - -signRightClick=Right click -signToUse=to use gate -signRandom=Random -signDisconnected=Disconnected \ No newline at end of file +bungeeDeny=Vous n'avez pas la permission de faire des Portail bungee ! +bungeeDisabled=La prise en charge de Bungeecord n'est pas activée. +bungeeEmpty=Legacy bungee vous demandent de spécifier un portail de destination et un serveur. +bungeeSign=Téléportation vers : +createConflict=Ce portail entre en conflit avec un portail existant. +createExists=Un portail portant ce nom existe déjà ! +createFull=Ce réseau est plein ! +createGateDeny=Vous n'avez pas accès à la disposition de ce Portail ! +createMsg=Portail créée +createNameLength=Nom trop court ou trop long ! +createNetDeny=Vous n'avez pas accès à ce réseau ! +createPersonal=Création d'un Portail sur le réseau personnel. +createWorldDeny=Vous n'avez pas accès à ce monde ! +denyMsg=Accès refusé ! +destEmpty=Liste de destination vide ! +destroyMsg=Portail détruit ! +ecoDeduct=Déduction de %cost%. +ecoInFunds=Fonds insuffisants ! +ecoLoad=Vault a été chargé, mais aucun plugin d'économie n'a pu être connecté +ecoObtain=Obtenu %cost% par StarGate %portal% ! +ecoRefund=%cost% remboursé +invalidMsg=Destination invalide ! +portalInfoDestination=Destination : %destination% +portalInfoName=Nom : %name% +portalInfoNetwork=%network% +portalInfoServer=%server% +portalInfoTitle=[STARGATE INFO] +prefix=[Stargate] +reloaded=Configuration rechargée ! +signDisconnected=Déconnecté +signInvalidGate=Portail invalide +signRandom=Aléatoire +signRightClick=Clic droit +signToUse=pour utiliser le portail +teleportMsg=Téléporté ! +vaultLoadError=L'économie est activée, mais Vault n'a pas pu être chargé. Économie désactivée +vaultLoaded=Vault v%version% trouvé \ No newline at end of file diff --git a/src/main/resources/lang/hu.txt b/src/main/resources/lang/hu.txt index dcfcdaf..a4ebc3e 100644 --- a/src/main/resources/lang/hu.txt +++ b/src/main/resources/lang/hu.txt @@ -1,28 +1,28 @@ +# Stargate Legacy Localisation File: Hungarian +# For a more intuitive interface, please use https://translate.sgrewritten.org + author=HPoltergeist -prefix=[Stargate] -teleportMsg=Teleportálás -destroyMsg=Kapu törölve -invalidMsg=Nincs ilyen cél blockMsg=Cél blokkolva -destEmpty=A cél lista üres -denyMsg=Hozzáférés megtagadva - -ecoDeduct=%cost% levonva -ecoRefund=%cost% visszatérítve -ecoObtain=%cost% bevétel a következő kapuból: %portal% -ecoInFunds=Nincs elég pénzed - +createConflict=Ez a kapu ütközik egy meglévö kapuval. +createExists=Már van ilyen nevű kapu! +createFull=Ez a hálózat tele van! +createGateDeny=Nincs hozzáférésed ahhoz a kapucsoporthoz! createMsg=Kapu elkészült -createNetDeny=Nincs hozzáférésed ahhoz a hálózathoz -createGateDeny=Nincs hozzáférésed ahhoz a kapucsoporthoz -createPersonal=Kapu készítése magán hálózaton -createNameLength=A név túl rövid vagy túl hosszú -createExists=Már van ilyen nevű kapu -createFull=Ez a hálózat tele van +createNameLength=A név túl rövid vagy túl hosszú! +createNetDeny=Nincs hozzáférésed ahhoz a hálózathoz! +createPersonal=Kapu készítése magán hálózaton. createWorldDeny=Nincs hozzáférésed ahhoz a világhoz -createConflict=Ez a kapu ütközik egy meglévö kapuval - +denyMsg=Hozzáférés megtagadva! +destEmpty=A cél lista üres! +destroyMsg=Kapu törölve! +ecoDeduct=%cost% levonva. +ecoInFunds=Nincs elég pénzed! +ecoObtain=%cost% bevétel a következő kapuból: %portal%! +ecoRefund=%cost% visszatérítve +invalidMsg=Nincs ilyen cél! +prefix=[Stargate] +signDisconnected=Szétkapcsolva +signRandom=Random signRightClick=Jobb katt a signToUse=használathoz -signRandom=Random -signDisconnected=Szétkapcsolva \ No newline at end of file +teleportMsg=Teleportálás! \ No newline at end of file diff --git a/src/main/resources/lang/it.txt b/src/main/resources/lang/it.txt index 1fa18e4..c50a124 100644 --- a/src/main/resources/lang/it.txt +++ b/src/main/resources/lang/it.txt @@ -1,28 +1,32 @@ -author=fr33soul -prefix=[Stargate] -teleportMsg=Teletrasporto -destroyMsg=Portale distrutto -invalidMsg=Destinazione invalida +# Stargate Legacy Localisation File: Italian +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=fr33soul & herbie_23 blockMsg=Destinazione bloccata -destEmpty=Lista destinazioni vuota -denyMsg=Accesso Negato - -ecoDeduct=%cost% pagato -ecoRefund=%cost% rimborsato -ecoObtain=%cost% ottenuto dal portale %portal% -ecoInFunds=Credito insufficiente - +bungeeDeny=Non sei autorizzato a creare portali Bungee! +bungeeDisabled=Il supporto a Bungeecord non è abilitato. +bungeeEmpty=I portali Bungee ereditari ti richiedono di specificare un portale e server di destinazione. +createConflict=Questo portale è in conflitto con un portale esistente. +createExists=Un portale con quel nome esiste già! +createFull=Quella rete è al completo! +createGateDeny=Non hai accesso a quella disposizione del portale! createMsg=Portale creato -createNetDeny=Non hai accesso a questa rete -createGateDeny=Non hai accesso a questo portale speciale -createPersonal=Creazione di un portale sulla rete personale -createNameLength=Nome troppo corto o troppo lungo. -createExists=Questo nome gia' esiste -createFull=Questa rete e' piena +createNameLength=Nome troppo breve o troppo lungo! +createNetDeny=Non hai accesso a quella rete! +createPersonal=Creazione del portale sulla rete personale. createWorldDeny=Non hai accesso a questo mondo -createConflict=Il portale crea conflitti con uno gia' esistente - -signRightClick=Right click -signToUse=to use gate -signRandom=Random -signDisconnected=Disconnected \ No newline at end of file +denyMsg=Accesso negato! +destEmpty=Elenco delle destinazioni vuoto! +destroyMsg=Portale distrutto! +ecoDeduct=%cost% detratto. +ecoInFunds=Fondi Insufficienti! +ecoObtain=%cost% ottenuto dallo StarGate %portal%! +ecoRefund=%cost% rimborsato +invalidMsg=Destinazione non valida! +prefix=[Stargate] +reloaded=Configurazione ricaricata! +signDisconnected=Disconnesso +signRandom=Casuale +signRightClick=Click destro +signToUse=usare portale +teleportMsg=Teletrasportato! \ No newline at end of file diff --git a/src/main/resources/lang/ja.txt b/src/main/resources/lang/ja.txt index 56c2b1d..1b7a0de 100644 --- a/src/main/resources/lang/ja.txt +++ b/src/main/resources/lang/ja.txt @@ -1,44 +1,42 @@ -author=furplag -prefix=[Stargate] -teleportMsg=テレポート -destroyMsg=ゲートが破壊されました -invalidMsg=無効な行き先 +# Stargate Legacy Localisation File: Japanese +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=Furplag & Translated s.r.l. blockMsg=ブロックされた行き先 -destEmpty=行き先リストが空です -denyMsg=アクセスが拒否されました -reloaded= Stargate をリロードしました - -ecoDeduct=%cost% の値引き -ecoRefund=%cost% の返金 -ecoObtain= Stargate %portal% から %cost% を得ました -ecoInFunds=資金の不足 -ecoLoadError= Vault が読み込まれましたが、Economy プラグインをフックできませんでした -vaultLoadError=Economy は有効になっていますが、Vault をロードできないため Economy は無効化されました -vaultLoaded= Vault v%version% が見つかりました - +bungeeDeny=バンジーゲートを作る権限がありません! +bungeeDisabled=Bungeecordサポートが有効になっていません。 +bungeeEmpty=従来のバンジーゲートでは、宛先ゲートとサーバーを指定する必要があります。 +bungeeSign=テレポート先: +createConflict=このゲートは既存のゲートと競合します。 +createExists=その名前のゲートはすでに存在しています! +createFull=ネットワークがいっぱいです! +createGateDeny=そのゲートレイアウトへのアクセス権を持っていません! createMsg=ゲートが作成されました -createNetDeny=対象のネットワークにアクセスできません -createGateDeny=対象のゲートレイアウトにアクセスできません -createPersonal=パーソナルネットワーク上にゲートを作成する -createNameLength=ゲート名が短すぎるか長すぎます -createExists=すでに存在するゲート名です -createFull=対象のネットワークはいっぱいです +createNameLength=名前が短すぎるか、長すぎます! +createNetDeny=そのネットワークへのアクセス権を持っていません! +createPersonal=パーソナルネットワーク上にゲートを作成します。 createWorldDeny=あなたはその世界にアクセスできません -createConflict=ゲートが既存のゲートと競合しています - -signRightClick=右クリック -signToUse=ゲートを使用する -signRandom=ランダム +denyMsg=アクセスが拒否されました! +destEmpty=目的地リストが空です! +destroyMsg=ゲートが破壊されました! +ecoDeduct=%cost%を差し引きました。 +ecoInFunds=お金が足りません! +ecoLoad=Vault が読み込まれましたが、Economy プラグインをフックできませんでした +ecoObtain=StarGate%portal%から%cost%を入手しました! +ecoRefund=%cost% の返金 +invalidMsg=目的地が無効です! +portalInfoDestination=行き先: %destination% +portalInfoName=ゲート名: %name% +portalInfoNetwork=ネットワーク: %network% +portalInfoServer=サーバー: %server% +portalInfoTitle=[STARGATE INFO] +prefix=[スターゲイト] +reloaded=設定をリロードしました! signDisconnected=切断 signInvalidGate=無効なゲート - -bungeeDisabled=BungeeCord サポートは無効になっています -bungeeDeny=BungeeCord ゲートを作成する権限がありません -bungeeEmpty=BungeeCord ゲートには、行き先とネットワークの両方が必要です -bungeeSign=テレポート先: - -portalInfoTitle=[STARGATE INFO] -portalInfoName=ゲート名: %name% -portalInfoDestination=行き先: %destination% -portalInfoNetwork=ネットワーク: %network% -portalInfoServer=サーバー: %server% +signRandom=ランダム +signRightClick=右クリック +signToUse=ゲートの使用方法 +teleportMsg=瞬間移動しました! +vaultLoadError=Economy は有効になっていますが、Vault をロードできないため Economy は無効化されました +vaultLoaded=Vault v%version% が見つかりました \ No newline at end of file diff --git a/src/main/resources/lang/nb-no.txt b/src/main/resources/lang/nb-no.txt deleted file mode 100644 index 0aff97f..0000000 --- a/src/main/resources/lang/nb-no.txt +++ /dev/null @@ -1,43 +0,0 @@ -author=EpicKnarvik97 -prefix=[Stjerneport] -teleportMsg=Teleporterte -destroyMsg=Port Ødelagt -invalidMsg=Ugyldig Destinasjon -blockMsg=Destinasjon Blokkert -destEmpty=Destinasjonslisten Er Tom -denyMsg=Tilgang Avslått -reloaded=Stjerneport Ble Lastet Inn På Nytt - -ecoDeduct=Fratrekk %cost% -ecoRefund=Refundert %cost% -ecoObtain=Fikk %cost% fra Stjerneport %portal% -ecoInFunds=Manglende Midler -ecoLoadError=Vault ble lastet inn men ingen brukbar økonomi-utvidelse ble funnet -vaultLoadError=Økonomi er skrudd på, men Vault kunne ikke lastes inn. Økonomi er skrudd av -vaultLoaded=Vault v%version% funnet - -createMsg=Port opprettet -createNetDeny=Du har ikke tilgang til det nettverket -createGateDeny=Du har ikke tilgang til den portutformingen -createPersonal=Oppretter port på personlig nettverk -createNameLength=Navnet er for kort eller for langt. -createExists=En port ved det navnet eksisterer allerede -createFull=Dette nettverket er fullt -createWorldDeny=Du har ikke tilgang til den verdenen -createConflict=Port er i konflikt med en eksisterende port - -signRightClick=Høyreklikk -signToUse=for å bruke port -signRandom=Tilfeldig -signDisconnected=Koblet fra - -bungeeDisabled=BungeeCord støtte er slått av. -bungeeDeny=Du har ikke tillatelse til å opprette BungeeCord porter. -bungeeEmpty=BungeeCord porter behøver bade en destinasjon og et nettverk. -bungeeSign=Teleporter til - -portalInfoTitle=[STJERNEPORT INFO] -portalInfoName=Navn: %name% -portalInfoDestination=Destinasjon: %destination% -portalInfoNetwork=Nettverk: %network% -portalInfoServer=Server: %server% \ No newline at end of file diff --git a/src/main/resources/lang/nb.txt b/src/main/resources/lang/nb.txt new file mode 100644 index 0000000..105f88d --- /dev/null +++ b/src/main/resources/lang/nb.txt @@ -0,0 +1,42 @@ +# Stargate Legacy Localisation File: Norwegian Bokmål +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=EpicKnarvik97 (Kristian Knarvik) +blockMsg=Destinasjon Blokkert +bungeeDeny=Du har ikke tilgang til å opprette bungee-porter! +bungeeDisabled=BungeeCord støtte er ikke skrudd på. +bungeeEmpty=Foreldede bungee porter krever at du spesifiserer en destinasjons-port or en server. +bungeeSign=Teleporter til +createConflict=Denne porten er i konflikt med en eksisterende port. +createExists=En port ved det navnet eksisterer allerede! +createFull=Det nettverket er fullt! +createGateDeny=Du har ikke tilgang til det port-formatet! +createMsg=Port opprettet +createNameLength=Navnet er for kort eller for langt! +createNetDeny=Du har ikke tilgang til det nettverket! +createPersonal=Oppretter port på personlig nettverk. +createWorldDeny=Du har ikke tilgang til den verdenen +denyMsg=Tilgang avslått! +destEmpty=Det fins ingen tilgjengelige destinasjoner! +destroyMsg=Port ødelagt! +ecoDeduct=%cost% ble trukket fra din konto. +ecoInFunds=Manglende midler! +ecoLoad=Vault ble lastet inn men ingen brukbar økonomi-utvidelse ble funnet +ecoObtain=%cost% ble tilført din konto fra Stjerne-port %portal%! +ecoRefund=Refundert %cost% +invalidMsg=Ugyldig destinasjon! +portalInfoDestination=Destinasjon: %destination% +portalInfoName=Navn: %name% +portalInfoNetwork=Nettverk: %network% +portalInfoServer=Server: %server% +portalInfoTitle=[STJERNEPORT INFO] +prefix=[Stargate] +reloaded=Konfigurasjonen ble lastet inn på nytt! +signDisconnected=Koblet fra +signInvalidGate=Ugyldig port +signRandom=Tilfeldig +signRightClick=Høyreklikk +signToUse=for å bruke port +teleportMsg=Teleporterte! +vaultLoadError=Økonomi er skrudd på, men Vault kunne ikke lastes inn. Økonomi er skrudd av +vaultLoaded=Vault v%version% funnet \ No newline at end of file diff --git a/src/main/resources/lang/nl.txt b/src/main/resources/lang/nl.txt index 2e29b3c..cd77869 100644 --- a/src/main/resources/lang/nl.txt +++ b/src/main/resources/lang/nl.txt @@ -1,28 +1,32 @@ -author=Grovert11 -prefix=[Stargate] -teleportMsg=Je bent geteleporteerd. -destroyMsg=Gate kapot gemaakt -invalidMsg=Ongeldige bestemming +# Stargate Legacy Localisation File: Dutch +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=Grovert11 & Matriarch! blockMsg=Bestemming geblokkeerd -destEmpty=Bestemmingslijst leeg -denyMsg=Toegang geweigerd - -ecoDeduct=%cost% afgeschreven -ecoRefund=%cost% terug gekregen -ecoObtain=%cost% gekregen van Stargate %portal% -ecoInFunds=Saldo niet toereikend - +bungeeDeny=Je hebt geen toestemming om bungee poorten te maken! +bungeeDisabled=Bungeecord ondersteuning is niet ingeschakeld. +bungeeEmpty=de verouderde Bungee gates vereisen dat je een specifieke bestemming naar poort en server opgeeft +createConflict=Deze poort conflicteert met een bestaande poort. +createExists=Er bestaat al een poort met die naam! +createFull=Er is geen ruimte op dat netwerk! +createGateDeny=U heeft geen toegang tot deze poort lay-out! createMsg=Gate gemaakt -createNetDeny=Je hebt geen toegang tot dat netwerk. -createGateDeny=Je mag die Gate-Layout niet gebruiken -createPersonal=Gate op persoonlijk netwerk gemaakt. -createNameLength=Naam te chosenLanguage of te kort. -createExists=Er bestaat al een gate met die naam -createFull=Dit netwerk is vol. +createNameLength=Naam te kort of te lang! +createNetDeny=Je hebt geen toegang tot dat netwerk! +createPersonal=Poort aanmaken op een persoonlijk netwerk. createWorldDeny=Je mag niet in die wereld komen. -createConflict=Gate maakt een conflict met een bestaande gate. - -signRightClick=Right click -signToUse=to use gate -signRandom=Random -signDisconnected=Disconnected \ No newline at end of file +denyMsg=Toegang geweigerd +destEmpty=Bestemming lijst is leeg! +destroyMsg=Poort vernietigd! +ecoDeduct=%cost% afgetrokken +ecoInFunds=Onvoldoende saldo! +ecoObtain=%cost% ontvangen van StarGate %portal%! +ecoRefund=%cost% terug gekregen +invalidMsg=Ongeldige bestemming! +prefix=[Stargate] +reloaded=Configuratie herladen! +signDisconnected=Verbinding verbroken +signRandom=Willekeurig +signRightClick=Rechter muis click +signToUse=Poort gebruiken +teleportMsg=Geteleporteerd! \ No newline at end of file diff --git a/src/main/resources/lang/nn-no.txt b/src/main/resources/lang/nn-no.txt deleted file mode 100644 index 7e07a23..0000000 --- a/src/main/resources/lang/nn-no.txt +++ /dev/null @@ -1,43 +0,0 @@ -author=EpicKnarvik97 -prefix=[Stjerneport] -teleportMsg=Teleporterte -destroyMsg=Port Øydelagd -invalidMsg=Ugyldig Destinasjon -blockMsg=Destinasjon Blokkert -destEmpty=Destinasjonslista Er Tom -denyMsg=Tilgang Avslått -reloaded=Stjerneport Vart Lasta Inn På Nytt - -ecoDeduct=Fråtrekk %cost% -ecoRefund=Refundert %cost% -ecoObtain=Mottok %cost% frå Stjerneport %portal% -ecoInFunds=Manglande Midlar -ecoLoadError=Vault vart lasta inn men inga brukbar økonomi-utviding vart funnen -vaultLoadError=Økonomi er skrudd på, men Vault kunne ikkje lastas inn. Økonomi er skrudd av -vaultLoaded=Vault v%version% funnen - -createMsg=Port oppretta -createNetDeny=Du har ikkje tilgang til det nettverket -createGateDeny=Du har ikkje tilgang til den portutforminga -createPersonal=Opprettar port på personleg nettverk -createNameLength=Namnet er for kort eller for langt. -createExists=Ein port med det namnet eksisterar allereie -createFull=Dette nettverket er fullt -createWorldDeny=Du har ikkje tilgang til den verda -createConflict=Port er i konflikt med ein eksisterande port - -signRightClick=Høgreklikk -signToUse=for å bruke port -signRandom=Tilfeldig -signDisconnected=Kopla frå - -bungeeDisabled=BungeeCord støtte er slått av. -bungeeDeny=Du har ikkje løyve til å opprette BungeeCord portar. -bungeeEmpty=BungeeCord portar treng bade ein destinasjon og eit nettverk. -bungeeSign=Teleporter til - -portalInfoTitle=[STJERNEPORT INFO] -portalInfoName=Namn: %name% -portalInfoDestination=Destinasjon: %destination% -portalInfoNetwork=Nettverk: %network% -portalInfoServer=Server: %server% \ No newline at end of file diff --git a/src/main/resources/lang/nn.txt b/src/main/resources/lang/nn.txt new file mode 100644 index 0000000..43abc00 --- /dev/null +++ b/src/main/resources/lang/nn.txt @@ -0,0 +1,42 @@ +# Stargate Legacy Localisation File: Norwegian Nynorsk +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=EpicKnarvik97 (Kristian Knarvik) +blockMsg=Destinasjon Blokkert +bungeeDeny=Du har ikkje tilgang til å oppretta bungee-portar! +bungeeDisabled=BungeeCord støtte er ikkje skrudd på. +bungeeEmpty=Forelda bungee portar krev at du spesifiserer ein destinasjons-port og ein tenarmaskin. +bungeeSign=Teleporter til +createConflict=Denne porten er i konflikt med ein eksisterande port. +createExists=Ein port ved det namnet eksisterer allereie! +createFull=Det nettverket er fullt! +createGateDeny=Du har ikkje tilgang til det port-formatet! +createMsg=Port opprettet +createNameLength=Namnet er for kort eller for langt! +createNetDeny=Du har ikkje tilgang til det nettverket! +createPersonal=Opprettar port på personleg nettverk. +createWorldDeny=Du har ikkje tilgang til den verda +denyMsg=Tilgang avslått! +destEmpty=Det finnast ingen tilgjengelege mål! +destroyMsg=Port Øydelagd! +ecoDeduct=%cost% vart trekt frå din konto. +ecoInFunds=Manglande midlar! +ecoLoad=Vault vart lasta inn men inga brukbar økonomi-utviding vart funnen +ecoObtain=%cost% vart tilført din konto frå Stjerne-port %portal%! +ecoRefund=Refundert %cost% +invalidMsg=Ugyldig mål! +portalInfoDestination=Destinasjon: %destination% +portalInfoName=%name% +portalInfoNetwork=Nettverk: %network% +portalInfoServer=Server: %server% +portalInfoTitle=[STJERNEPORT INFO] +prefix=[Stjerneport] +reloaded=Konfigurasjonen vart lasta inn på nytt! +signDisconnected=Fråkopla +signInvalidGate=Ugyldig gate +signRandom=Tilfeldig +signRightClick=Høgre-klikk +signToUse=for å bruka port +teleportMsg=Teleporterte! +vaultLoadError=Økonomi er skrudd på, men Vault kunne ikkje lastas inn. Økonomi er skrudd av +vaultLoaded=Vault v%version% funnen \ No newline at end of file diff --git a/src/main/resources/lang/pt-br.txt b/src/main/resources/lang/pt.txt similarity index 82% rename from src/main/resources/lang/pt-br.txt rename to src/main/resources/lang/pt.txt index 594e96c..d53ed72 100644 --- a/src/main/resources/lang/pt-br.txt +++ b/src/main/resources/lang/pt.txt @@ -1,28 +1,28 @@ +# Stargate Legacy Localisation File: Portuguese +# For a more intuitive interface, please use https://translate.sgrewritten.org + author=TheProbleemm -prefix=[Stargate] -teleportMsg=Voce foi teleportado. -destroyMsg=Portal destruido. -invalidMsg=Destino Nao conectado/invalido. blockMsg=Destino bloqueado. -destEmpty=Lista de destino vazia. -denyMsg=Acesso negado. Voce nao tem permissao. - -ecoDeduct=O teleporte custou: R$ %cost%. -ecoRefund=Voce foi reembolsado: R$ %cost%. -ecoObtain=Foi obtido R$ %cost% do portal %portal%. -ecoInFunds=Fundos insuficientes. - -createMsg=Portal criado. -createNetDeny=Voce nao tem acesso a essa rede. -createGateDeny=Voce nao tem acesso a esse portal layout. -createPersonal=Criando portal em rede pessoal. -createNameLength=Nome muito curto ou muito grande. +createConflict=Portal em conflito com um portal ja existente. createExists=Já existe um portal com esse nome. createFull=Esta rede esta cheia. +createGateDeny=Voce nao tem acesso a esse portal layout. +createMsg=Portal criado. +createNameLength=Nome muito curto ou muito grande. +createNetDeny=Voce nao tem acesso a essa rede. +createPersonal=Criando portal em rede pessoal. createWorldDeny=Voce nao tem acesso a esse mundo. -createConflict=Portal em conflito com um portal ja existente. - +denyMsg=Acesso negado. Voce nao tem permissao. +destEmpty=Lista de destino vazia. +destroyMsg=Portal destruido. +ecoDeduct=O teleporte custou: R$ %cost%. +ecoInFunds=Fundos insuficientes. +ecoObtain=Foi obtido R$ %cost% do portal %portal%. +ecoRefund=Voce foi reembolsado: R$ %cost%. +invalidMsg=Destino Nao conectado/invalido. +prefix=[Stargate] +signDisconnected=Disconnected +signRandom=Random signRightClick=Right click signToUse=to use gate -signRandom=Random -signDisconnected=Disconnected \ No newline at end of file +teleportMsg=Voce foi teleportado. \ No newline at end of file diff --git a/src/main/resources/lang/ru.txt b/src/main/resources/lang/ru.txt index afef3c7..acf5855 100644 --- a/src/main/resources/lang/ru.txt +++ b/src/main/resources/lang/ru.txt @@ -1,28 +1,28 @@ +# Stargate Legacy Localisation File: Russian +# For a more intuitive interface, please use https://translate.sgrewritten.org + author=ckr@jk -prefix=[Портал] -teleportMsg=Вы перемещены -destroyMsg=Портал уничтожен -invalidMsg=Недопустимый пункт назначения -blockMsg=Пункт назначения заблокирован -destEmpty=Список пунктов назначения пуст -denyMsg=Доступ запрещен - -ecoDeduct=Вычтено %cost% -ecoRefund=Возвращено %cost% -ecoObtain=Получено %cost% с портала %portal% -ecoInFunds=Недостаточно средств - +blockMsg=Пункт назначения заблокирован! +createConflict=Портал конфликтует с уже существующим порталом. +createExists=Портал с таким именем уже существует! +createFull=Эта сеть порталов заполнена! +createGateDeny=У вас нет доступа к размещению портала! createMsg=Портал создан -createNetDeny=У вас нет доступа к этой сети порталов -createGateDeny=У вас нет доступа к размещению портала -createPersonal=Создание портала с персональной сетью -createNameLength=Имя слишком короткое или слишком длинное -createExists=Портал с таким именем уже существует -createFull=Эта сеть порталов заполнена -createWorldDeny=У вас нет доступа к этому миру -createConflict=Портал конфликтует с уже существующим порталом - +createNameLength=Имя слишком короткое или слишком длинное! +createNetDeny=У вас нет доступа к этой сети порталов! +createPersonal=Создание портала с персональной сетью. +createWorldDeny=У вас нет доступа к этому миру! +denyMsg=Доступ запрещен! +destEmpty=Список пунктов назначения пуст! +destroyMsg=Портал уничтожен! +ecoDeduct=Вычтено %cost%. +ecoInFunds=Недостаточно средств! +ecoObtain=Получено %cost% с портала %portal%! +ecoRefund=Возвращено %cost%. +invalidMsg=Недопустимый пункт назначения! +prefix=[Портал] +signDisconnected=Disconnected +signRandom=Random signRightClick=Right click signToUse=to use gate -signRandom=Random -signDisconnected=Disconnected \ No newline at end of file +teleportMsg=Вы перемещены! \ No newline at end of file diff --git a/src/main/resources/lang/sv.txt b/src/main/resources/lang/sv.txt new file mode 100644 index 0000000..a2c8837 --- /dev/null +++ b/src/main/resources/lang/sv.txt @@ -0,0 +1,42 @@ +# Stargate Legacy Localisation File: Swedish +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=Thorinwasher +blockMsg=Destination är blockerad +bungeeDeny=Du har inte behörighet att bygga bungee-portar! +bungeeDisabled=Stöd för bungeecord är inte aktiverat. +bungeeEmpty=Äldre bungee gates kräver att du anger en destination gate och server. +bungeeSign=Teleportera till +createConflict=Denna port står i konflikt med en befintlig port. +createExists=En port med det namnet existerar redan. +createFull=Det nätverket är redan fullt! +createGateDeny=Du har inte tillgång till den port-sorten! +createMsg=Port skapad +createNameLength=Namnet är för kort eller för långt! +createNetDeny=Du har inte tillgång till det nätverket! +createPersonal=Port skapad på personligt nätverk. +createWorldDeny=Du har inte tillgång till den världen +denyMsg=Åtkomst nekad! +destEmpty=Destinationslistan är tom! +destroyMsg=Port förstörd! +ecoDeduct=Avdrog %cost%. +ecoInFunds=Inte tillräckligt med pengar. +ecoLoad=Vault laddades, men inget ekonomi-plugin kunde fästas in i +ecoObtain=Erhöll %cost% från StarGate %portal%! +ecoRefund=Återbetald %cost% +invalidMsg=Ogiltig destination! +portalInfoDestination=Destination: %destination% +portalInfoName=Namn: %name% +portalInfoNetwork=Nätverk: %network% +portalInfoServer=Server: %server% +portalInfoTitle=[STARGATE INFO] +prefix=[Stargate] +reloaded=Konfigurationen laddades om! +signDisconnected=Frånkopplad +signInvalidGate=Ogiltig port +signRandom=Slumpad +signRightClick=Högerklicka för +signToUse=att aktivera port +teleportMsg=Teleporterad! +vaultLoadError=Ekonomi är aktiverat men Vault kunde inte laddas. Ekonomi inaktiverat +vaultLoaded=Vault v%version% hittades \ No newline at end of file diff --git a/src/main/resources/lang/tr.txt b/src/main/resources/lang/tr.txt new file mode 100644 index 0000000..57c4779 --- /dev/null +++ b/src/main/resources/lang/tr.txt @@ -0,0 +1,42 @@ +# Stargate Legacy Localisation File: Turkish +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=yugurr & axolotlagatsuma +blockMsg=İstikamet engellenmiş. +bungeeDeny=Bungee geçitleri yapmak için yetkiniz yok! +bungeeDisabled=Bungeecord desteği aktif değil. +bungeeEmpty=Eski bungee geçitleri senden sunucunun ve geçitin hedefini belirlemeni gerektirir. +bungeeSign=Işınlan +createConflict=Bu geçit var olan geçitle çakışmaktadır. +createExists=Bu isimde zaten bir geçit mevcut. +createFull=Bu ağ dolu! +createGateDeny=Bu geçit düzenine erişim izniniz yok! +createMsg=Geçit Oluşturuldu +createNameLength=İsim çok kısa ya da çok uzun! +createNetDeny=Bu ağa erişim izniniz yok. +createPersonal=Kişisel ağda geçit yaratılıyor. +createWorldDeny=Bu dünyaya erişim izniniz yok. +denyMsg=Erişim reddedildi! +destEmpty=Hedef listesi boş! +destroyMsg=Geçit yıkıldı! +ecoDeduct=Düşürülmüş %cost%. +ecoInFunds=Yetersiz Bakiye! +ecoLoad=Vault yüklendi ama bağlanabileceği bir ekonomi eklentisi bulunamadı. +ecoObtain=Stargarte %portal% 'ından %cost% elde edildi! +ecoRefund=%cost% iade edildi +invalidMsg=Geçersiz Hedef! +portalInfoDestination=İstikamet: %destination% +portalInfoName=Ad: %name% +portalInfoNetwork=Ağ: %network% +portalInfoServer=Sunucu: %server% +portalInfoTitle=[STARGATE BİLGİ] +prefix=[Stargate] +reloaded=Yapılandırma yeniden yüklendi! +signDisconnected=Bağlantı Kesildi +signInvalidGate=Geçersiz geçit +signRandom=Rastgele +signRightClick=Sağ tık +signToUse=geçiti kullanmak için +teleportMsg=Işınlanıldı! +vaultLoadError=Ekonomi aktif ama Vault yüklenemedi. Ekonomi pasif +vaultLoaded=Vault v%version% bulundu \ No newline at end of file diff --git a/src/main/resources/lang/uk.txt b/src/main/resources/lang/uk.txt new file mode 100644 index 0000000..f07bb05 --- /dev/null +++ b/src/main/resources/lang/uk.txt @@ -0,0 +1,28 @@ +# Stargate Legacy Localisation File: Ukrainian +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=Veydzher +bungeeDeny=Ви не маєте дозволу робити шлюзи Bungee! +bungeeDisabled=Підтримка Bungeecord не ввімкнена. +bungeeEmpty=Застарілі шлюзи Bungee вимагають, щоб ви вказали шлюз призначення та сервер. +createConflict=Цей шлюз конфліктує з наявним шлюзом. +createExists=Шлюз з такою назвою вже існує! +createFull=Ця мережа повна! +createGateDeny=Ви не маєте доступу до цього макета шлюзу! +createNameLength=Назва занадто коротка або довга! +createNetDeny=Ви не маєте доступу до цієї мережі! +createPersonal=Створення шлюзу в персональній мережі. +denyMsg=У доступі відмовлено! +destEmpty=Список пунктів призначення порожній! +destroyMsg=Шлюз знищено! +ecoDeduct=Вираховано %cost%. +ecoInFunds=Недостатньо коштів! +ecoObtain=Отримано %cost% на StarGate %portal%! +invalidMsg=Недійсний пункт призначення! +prefix=[Stargate] +reloaded=Конфігурацію перезавантажено! +signDisconnected=Відключено +signRandom=Випадковий +signRightClick=ПКМ, щоб +signToUse=використов. шлюз +teleportMsg=Телепортовано! \ No newline at end of file diff --git a/src/main/resources/lang/zh.txt b/src/main/resources/lang/zh.txt new file mode 100644 index 0000000..a066e87 --- /dev/null +++ b/src/main/resources/lang/zh.txt @@ -0,0 +1,42 @@ +# Stargate Legacy Localisation File: Chinese +# For a more intuitive interface, please use https://translate.sgrewritten.org + +author=YK_DZ +blockMsg=目的地被阻挡 +bungeeDeny=你没有创建跨服星门的权限! +bungeeDisabled=跨服功能未启用. +bungeeEmpty=传统跨服星门需要指定目的地和服务器名. +bungeeSign=传送到 +createConflict=这个星门与现有星门冲突. +createExists=本星门网络中已存在一个同名星门! +createFull=该星门网络已满! +createGateDeny=你没有这个星门结构的使用许可! +createMsg=星门已创建 +createNameLength=名称太短或太长! +createNetDeny=你没有这个星门网络的访问许可! +createPersonal=在私人网络中创建了星门. +createWorldDeny=你没有链接这个世界的权限 +denyMsg=访问被拒绝! +destEmpty=目的地列表为空! +destroyMsg=星门已被摧毁! +ecoDeduct=花费了 %cost%. +ecoInFunds=存款不足! +ecoLoad=Vault 已加载, 但未检测到合适的经济插件 +ecoObtain=从星门 %portal% 赚取了 %cost%! +ecoRefund=退款 %cost% +invalidMsg=无效的目的地! +portalInfoDestination=目的地: %destination% +portalInfoName=名称: %name% +portalInfoNetwork=星门网络: %network% +portalInfoServer=服务器: %server% +portalInfoTitle=[星门信息] +prefix=[星门] +reloaded=配置文件已重载! +signDisconnected=断开连接 +signInvalidGate=未知星门 +signRandom=随机 +signRightClick=右键 +signToUse=以使用星门 +teleportMsg=已传送! +vaultLoadError=未检测到Vault. 经济模块已禁用 +vaultLoaded=检测到 Vault v%version% \ No newline at end of file diff --git a/src/main/resources/lang/zh_cn.txt b/src/main/resources/lang/zh_cn.txt deleted file mode 100644 index 095fa24..0000000 --- a/src/main/resources/lang/zh_cn.txt +++ /dev/null @@ -1,39 +0,0 @@ -author=YKDZ -signRightClick=右键 -ecoLoadError=Vault 已加载, 但未检测到合适的经济插件 -createConflict=星门与现有星门冲突 -invalidMsg=无效的目的地 -prefix=[星门] -ecoObtain=从星门 %portal% 收取了 %cost% -vaultLoaded=检测到 Vault v%version% -reloaded=星门插件已重载 -bungeeDeny=你没有创建跨服星门的权限. -signToUse=以使用星门 -signInvalidGate=未知星门 -bungeeEmpty=跨服星门需要提供目的地和网络. -createMsg=星门已创建 -bungeeDisabled=跨服功能已被禁用. -blockMsg=目的地被阻挡 -ecoInFunds=余额不足 -createNameLength=名称过短或过长. -vaultLoadError=未检测到Vault. 经济模块已禁用 -denyMsg=访问被拒 -ecoDeduct=花费 %cost% -signDisconnected=已取消链接 -createNetDeny=你没有这个星门网络的许可 -bungeeSign=传送到 -portalInfoName=名称: %name% -destroyMsg=星门已被破坏 -portalInfoTitle=[星门信息] -createExists=与已有星门重名 -teleportMsg=已传送 -createGateDeny=你没有使用这个星门结构的权限 -signRandom=随机 -portalInfoServer=服务器: %server% -createWorldDeny=你没有链接这个世界的权限 -portalInfoDestination=目的地: %destination% -portalInfoNetwork=星门网络: %network% -destEmpty=目的地列表为空 -createPersonal=在私人网络中创建星门 -ecoRefund=退款 %cost% -createFull=此星门网络已满 \ No newline at end of file diff --git a/src/main/resources/messages/about.md b/src/main/resources/messages/about.md new file mode 100644 index 0000000..a6202ed --- /dev/null +++ b/src/main/resources/messages/about.md @@ -0,0 +1,10 @@ +&green&Stargate Unified Legacy v. &gold&@version@ &green&(maintained by&gold& [sgrewritten.org](https://sgrewritten.org) +&green&). +A Hmod transportation concept by&gold& [Dinnerbone](https://github.com/Dinnerbone)&green& +and&gold& [Sturmeh](https://github.com/Sturmeh). +&green&Ported to bukkit by&gold& [Drakia](https://github.com/DrakiaXYZ).&green& Revived +by&gold& [EpicKnarvik97](https://sgrewritten.org/knarvik), +&green&now incorporating features from all other Stargate forks. + +For more information, visit&gold& [sgrewritten.org/legacy](https://sgrewritten.org/legacy) +&green&For current versions, visit&gold& [sgrewritten.org/download](https://sgrewritten.org/download) \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 283fe85..cdf03ff 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,12 +1,12 @@ name: Stargate main: net.knarcraft.stargate.Stargate version: '${project.version}' -description: Stargate mod for Bukkit Revived +description: The original, and still the best, MineCraft transportation solution (Legacy Version) author: EpicKnarvik97 authors: [ Drakia, PseudoKnight, EpicKnarvik97 ] -website: https://git.knarcraft.net/EpicKnarvik97/Stargate -api-version: 1.18 -softdepend: [ Vault, dynmap ] +website: https://sgrewritten.org/legacygit +api-version: 1.20 +softdepend: [ Vault, dynmap, Geyser-Spigot, floodgate ] commands: stargate: aliases: @@ -30,7 +30,7 @@ permissions: default: false stargate.use: description: Allow use of all stargates linking to any world in any network - default: true + default: false children: stargate.world: true stargate.network: true @@ -43,7 +43,7 @@ permissions: default: false stargate.create: description: Allow creating stargates on any network using any gate type - default: op + default: false children: stargate.create.personal: true stargate.create.network: true @@ -59,7 +59,7 @@ permissions: default: false stargate.destroy: description: Allows the destruction of all stargates - default: op + default: false children: stargate.destroy.network: true stargate.destroy.personal: true @@ -71,7 +71,7 @@ permissions: default: false stargate.free: description: Allow free use/creation/destruction of stargates - default: op + default: false children: stargate.free.use: true stargate.free.create: true @@ -87,7 +87,7 @@ permissions: default: false stargate.option: description: Allows use of all options - default: op + default: false children: stargate.option.hidden: true stargate.option.alwayson: true @@ -97,7 +97,7 @@ permissions: stargate.option.show: true stargate.option.nonetwork: true stargate.option.random: true - stargate.option.silent: true + stargate.option.quiet: true stargate.option.nosign: true stargate.option.hidden: description: Allows the creation of a hidden stargate @@ -123,7 +123,7 @@ permissions: stargate.option.random: description: Allows the creation of a stargate with a random destination default: false - stargate.option.silent: + stargate.option.quiet: description: Allows the creation of a stargate which does not output anything to the chat default: false stargate.option.nosign: @@ -149,11 +149,35 @@ permissions: default: false stargate.admin: description: Allow all admin features and commands (Hidden/Private bypass, BungeeCord, Reload, Config) - default: op + default: false children: stargate.admin.reload: true stargate.admin.hidden: true stargate.admin.private: true stargate.admin.bungee: true stargate.admin.config: true - stargate.admin.dye: true \ No newline at end of file + stargate.admin.dye: true + stargate.group.admin: + description: Allow everything except stargate.free + default: op + children: + stargate.admin: true + stargate.server: true + stargate.group.builder: true + stargate.group.builder: + description: Allows unrestricted building and destruction of Stargates with any option + default: false + children: + stargate.group.player: true + stargate.option: true + stargate.network: true + stargate.create: true + stargate.destroy: true + stargate.group.player: + description: Gives normal players limited use of Stargates + default: true + children: + stargate.use: true + stargate.create.gate.nethergate: true + stargate.create.personal: true + stargate.destroy.personal: true \ No newline at end of file diff --git a/src/test/java/net/knarcraft/stargate/BlockLocationTest.java b/src/test/java/net/knarcraft/stargate/BlockLocationTest.java deleted file mode 100644 index 0935057..0000000 --- a/src/test/java/net/knarcraft/stargate/BlockLocationTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package net.knarcraft.stargate; - -import be.seeseemelk.mockbukkit.MockBukkit; -import be.seeseemelk.mockbukkit.WorldMock; -import net.knarcraft.stargate.container.BlockLocation; -import org.bukkit.Material; -import org.junit.After; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; - -public class BlockLocationTest { - private WorldMock mockWorld; - - @BeforeEach - public void setUp() { - mockWorld = new WorldMock(Material.DIRT, 5); - } - - @After - public void tearDown() { - MockBukkit.unmock(); - } - - @Test - public void equalsTest() { - BlockLocation location1 = new BlockLocation(mockWorld, 1, 3, 4); - BlockLocation location2 = new BlockLocation(mockWorld, 1, 3, 4); - assertEquals(location1, location2); - } - - @Test - public void notEqualsTest1() { - BlockLocation location1 = new BlockLocation(mockWorld, 1, 3, 4); - BlockLocation location2 = new BlockLocation(mockWorld, 2, 3, 4); - assertNotEquals(location1, location2); - } - - @Test - public void notEqualsTest2() { - BlockLocation location1 = new BlockLocation(mockWorld, 1, 3, 4); - BlockLocation location2 = new BlockLocation(mockWorld, 1, 5, 4); - assertNotEquals(location1, location2); - } - - @Test - public void notEqualsTest3() { - BlockLocation location1 = new BlockLocation(mockWorld, 1, 3, 4); - BlockLocation location2 = new BlockLocation(mockWorld, 1, 3, 7); - assertNotEquals(location1, location2); - } - - @Test - public void notEqualsTest4() { - BlockLocation location1 = new BlockLocation(mockWorld, 1, 3, 4); - BlockLocation location2 = new BlockLocation(new WorldMock(Material.DIRT, 4), 1, 3, 4); - assertNotEquals(location1, location2); - } - - @Test - public void makeRelativeTest() { - BlockLocation location = new BlockLocation(mockWorld, 3, 7, 19); - BlockLocation newLocation = location.makeRelativeBlockLocation(34, 65, 75); - assertEquals(37, newLocation.getBlockX()); - assertEquals(72, newLocation.getBlockY()); - assertEquals(94, newLocation.getBlockZ()); - } - - @Test - public void materialTest() { - BlockLocation location = new BlockLocation(mockWorld, 0, 0, 0); - assertNotEquals(Material.BOOKSHELF, location.getType()); - location.setType(Material.BOOKSHELF); - assertEquals(Material.BOOKSHELF, location.getType()); - } - - @Test - public void toStringTest() { - BlockLocation location = new BlockLocation(mockWorld, 56, 87, 34); - assertEquals("56,87,34", location.toString()); - } - -} diff --git a/src/test/java/net/knarcraft/stargate/RelativeBlockVectorTest.java b/src/test/java/net/knarcraft/stargate/RelativeBlockVectorTest.java deleted file mode 100644 index f75aefe..0000000 --- a/src/test/java/net/knarcraft/stargate/RelativeBlockVectorTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.knarcraft.stargate; - -import net.knarcraft.stargate.container.RelativeBlockVector; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; - -public class RelativeBlockVectorTest { - - @Test - public void getTest() { - RelativeBlockVector relativeBlockVector = new RelativeBlockVector(56, 44, 23); - assertEquals(56, relativeBlockVector.getRight()); - assertEquals(44, relativeBlockVector.getDown()); - assertEquals(23, relativeBlockVector.getOut()); - } - - @Test - public void equalsTest() { - RelativeBlockVector vector1 = new RelativeBlockVector(56, 34, 76); - RelativeBlockVector vector2 = new RelativeBlockVector(56, 34, 76); - assertEquals(vector1, vector2); - } - - @Test - public void notEqualsTest() { - RelativeBlockVector vector1 = new RelativeBlockVector(456, 78, 234); - RelativeBlockVector vector2 = new RelativeBlockVector(56, 34, 76); - assertNotEquals(vector1, vector2); - } - -} diff --git a/src/test/java/net/knarcraft/stargate/container/BlockLocationTest.java b/src/test/java/net/knarcraft/stargate/container/BlockLocationTest.java deleted file mode 100644 index 520627a..0000000 --- a/src/test/java/net/knarcraft/stargate/container/BlockLocationTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package net.knarcraft.stargate.container; - -import be.seeseemelk.mockbukkit.WorldMock; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class BlockLocationTest { - - @Test - public void makeRelativeBlockLocationTest() { - WorldMock world = new WorldMock(); - BlockLocation startLocation = new BlockLocation(world, 5, 4, 3); - - //Move to some other, different location - BlockLocation relativeLocation = startLocation.makeRelativeBlockLocation(4, 6, 8); - Assertions.assertNotEquals(startLocation, relativeLocation); - - //Move back to make sure we can go back to where we started by going in the opposite direction - BlockLocation sameAsStartLocation = relativeLocation.makeRelativeBlockLocation(-4, -6, -8); - Assertions.assertEquals(startLocation, sameAsStartLocation); - } - - @Test - public void getRelativeLocationTest() { - WorldMock world = new WorldMock(); - BlockLocation startLocation = new BlockLocation(world, 7, 3, 6); - - RelativeBlockVector relativeBlockVector = new RelativeBlockVector(2, 1, 3); - BlockLocation relativeLocation1 = startLocation.getRelativeLocation(relativeBlockVector, 0); - //With yaw = 0, going right goes in the x direction, and out goes in the z direction, while y is decremented - BlockLocation targetLocation1 = new BlockLocation(world, 9, 2, 9); - Assertions.assertEquals(targetLocation1, relativeLocation1); - - BlockLocation relativeLocation2 = startLocation.getRelativeLocation(relativeBlockVector, 90); - //With yaw = 90, going right goes in the z direction, and out goes in the -x direction, while y is decremented - BlockLocation targetLocation2 = new BlockLocation(world, 4, 2, 8); - Assertions.assertEquals(targetLocation2, relativeLocation2); - - BlockLocation relativeLocation3 = startLocation.getRelativeLocation(relativeBlockVector, 180); - //With yaw = 180, going right goes in the -x direction, and out goes in the -z direction, while y is decremented - BlockLocation targetLocation3 = new BlockLocation(world, 5, 2, 3); - Assertions.assertEquals(targetLocation3, relativeLocation3); - - BlockLocation relativeLocation4 = startLocation.getRelativeLocation(relativeBlockVector, 270); - //With yaw = 270, going right goes in the -z direction, and out goes in the x direction, while y is decremented - BlockLocation targetLocation4 = new BlockLocation(world, 10, 2, 4); - Assertions.assertEquals(targetLocation4, relativeLocation4); - } - -} diff --git a/src/test/java/net/knarcraft/stargate/container/RelativeBlockVectorTest.java b/src/test/java/net/knarcraft/stargate/container/RelativeBlockVectorTest.java index 8259b37..5406053 100644 --- a/src/test/java/net/knarcraft/stargate/container/RelativeBlockVectorTest.java +++ b/src/test/java/net/knarcraft/stargate/container/RelativeBlockVectorTest.java @@ -1,8 +1,9 @@ package net.knarcraft.stargate.container; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class RelativeBlockVectorTest { @Test @@ -15,14 +16,14 @@ public class RelativeBlockVectorTest { for (int i = 0; i < 1000; i++) { int randomValue = getRandomNumber(); - RelativeBlockVector newVector = relativeBlockVector.addToVector(RelativeBlockVector.Property.RIGHT, randomValue); - Assertions.assertEquals(new RelativeBlockVector(right + randomValue, down, out), newVector); + RelativeBlockVector newVector = relativeBlockVector.addRight(randomValue); + assertEquals(new RelativeBlockVector(right + randomValue, down, out), newVector); - newVector = relativeBlockVector.addToVector(RelativeBlockVector.Property.OUT, randomValue); - Assertions.assertEquals(new RelativeBlockVector(right, down, out + randomValue), newVector); + newVector = relativeBlockVector.addOut(randomValue); + assertEquals(new RelativeBlockVector(right, down, out + randomValue), newVector); - newVector = relativeBlockVector.addToVector(RelativeBlockVector.Property.DOWN, randomValue); - Assertions.assertEquals(new RelativeBlockVector(right, down + randomValue, out), newVector); + newVector = relativeBlockVector.addDown(randomValue); + assertEquals(new RelativeBlockVector(right, down + randomValue, out), newVector); } } @@ -37,7 +38,7 @@ public class RelativeBlockVectorTest { randomNumber3); RelativeBlockVector invertedBlockVector = new RelativeBlockVector(-randomNumber1, -randomNumber2, -randomNumber3); - Assertions.assertEquals(invertedBlockVector, relativeBlockVector.invert()); + assertEquals(invertedBlockVector, relativeBlockVector.invert()); } } diff --git a/src/test/java/net/knarcraft/stargate/portal/GateLayoutTest.java b/src/test/java/net/knarcraft/stargate/portal/GateLayoutTest.java deleted file mode 100644 index c8bf07c..0000000 --- a/src/test/java/net/knarcraft/stargate/portal/GateLayoutTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package net.knarcraft.stargate.portal; - -import be.seeseemelk.mockbukkit.MockBukkit; -import be.seeseemelk.mockbukkit.ServerMock; -import be.seeseemelk.mockbukkit.WorldMock; -import net.knarcraft.stargate.Stargate; -import net.knarcraft.stargate.container.RelativeBlockVector; -import net.knarcraft.stargate.portal.property.gate.GateHandler; -import net.knarcraft.stargate.portal.property.gate.GateLayout; -import org.bukkit.Material; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class GateLayoutTest { - - private static GateLayout layout; - - @BeforeAll - public static void setUp() { - ServerMock server = MockBukkit.mock(); - server.addWorld(new WorldMock(Material.DIRT, 5)); - MockBukkit.load(Stargate.class); - layout = GateHandler.getGateByName("nethergate.gate").getLayout(); - } - - @AfterAll - public static void tearDown() { - MockBukkit.getMock().getPluginManager().disablePlugins(); - MockBukkit.unmock(); - } - - @Test - public void gateLayoutExitTest() { - assertEquals(new RelativeBlockVector(1, 3, 0), layout.getExit()); - } - - @Test - public void gateLayoutExitsTest() { - List expected = new ArrayList<>(); - expected.add(new RelativeBlockVector(1, 3, 0)); - expected.add(new RelativeBlockVector(2, 3, 0)); - - List exits = layout.getExits(); - exits.forEach((blockVector) -> assertTrue(expected.contains(blockVector))); - } - - @Test - public void gateLayoutBorderTest() { - List expected = new ArrayList<>(); - expected.add(new RelativeBlockVector(1, 0, 0)); - expected.add(new RelativeBlockVector(2, 0, 0)); - expected.add(new RelativeBlockVector(0, 1, 0)); - expected.add(new RelativeBlockVector(0, 2, 0)); - expected.add(new RelativeBlockVector(0, 3, 0)); - expected.add(new RelativeBlockVector(1, 4, 0)); - expected.add(new RelativeBlockVector(2, 4, 0)); - expected.add(new RelativeBlockVector(3, 1, 0)); - expected.add(new RelativeBlockVector(3, 2, 0)); - expected.add(new RelativeBlockVector(3, 3, 0)); - - RelativeBlockVector[] borderBlocks = layout.getBorder(); - for (RelativeBlockVector blockVector : borderBlocks) { - assertTrue(expected.contains(blockVector)); - } - } - - @Test - public void gateLayoutControlsTest() { - List expected = new ArrayList<>(); - expected.add(new RelativeBlockVector(0, 2, 0)); - expected.add(new RelativeBlockVector(3, 2, 0)); - - RelativeBlockVector[] controlBlocks = layout.getControls(); - for (RelativeBlockVector blockVector : controlBlocks) { - assertTrue(expected.contains(blockVector)); - } - } - - @Test - public void gateLayoutEntrancesTest() { - List expected = new ArrayList<>(); - expected.add(new RelativeBlockVector(1, 1, 0)); - expected.add(new RelativeBlockVector(2, 1, 0)); - expected.add(new RelativeBlockVector(1, 2, 0)); - expected.add(new RelativeBlockVector(2, 2, 0)); - expected.add(new RelativeBlockVector(1, 3, 0)); - expected.add(new RelativeBlockVector(2, 3, 0)); - - RelativeBlockVector[] controlBlocks = layout.getEntrances(); - for (RelativeBlockVector blockVector : controlBlocks) { - assertTrue(expected.contains(blockVector)); - } - } - -} diff --git a/src/test/java/net/knarcraft/stargate/utility/DirectionHelperTest.java b/src/test/java/net/knarcraft/stargate/utility/DirectionHelperTest.java deleted file mode 100644 index 6de3716..0000000 --- a/src/test/java/net/knarcraft/stargate/utility/DirectionHelperTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.knarcraft.stargate.utility; - -import be.seeseemelk.mockbukkit.WorldMock; -import org.bukkit.Location; -import org.bukkit.World; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class DirectionHelperTest { - - @Test - public void getYawFromLocationTest() { - World world = new WorldMock(); - Location location1 = new Location(world, 100, 0, 100); - Location location2 = new Location(world, 100, 0, 101); - - double yaw = DirectionHelper.getYawFromLocationDifference(location1, location2); - Assertions.assertEquals(0, yaw); - - location2 = new Location(world, 100, 0, 99); - yaw = DirectionHelper.getYawFromLocationDifference(location1, location2); - Assertions.assertEquals(180, yaw); - - location2 = new Location(world, 101, 0, 100); - yaw = DirectionHelper.getYawFromLocationDifference(location1, location2); - Assertions.assertEquals(270, yaw); - - location2 = new Location(world, 99, 0, 100); - yaw = DirectionHelper.getYawFromLocationDifference(location1, location2); - Assertions.assertEquals(90, yaw); - } - -} diff --git a/src/test/java/net/knarcraft/stargate/utility/MaterialHelperTest.java b/src/test/java/net/knarcraft/stargate/utility/MaterialHelperTest.java deleted file mode 100644 index f8481b2..0000000 --- a/src/test/java/net/knarcraft/stargate/utility/MaterialHelperTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package net.knarcraft.stargate.utility; - -import be.seeseemelk.mockbukkit.MockBukkit; -import org.bukkit.Material; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class MaterialHelperTest { - - @BeforeAll - public static void setUp() { - MockBukkit.mock(); - } - - @AfterAll - public static void tearDown() { - MockBukkit.unmock(); - } - - @Test - public void isWallCoralTest() { - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.DEAD_BRAIN_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.BRAIN_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.DEAD_BUBBLE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.BUBBLE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.DEAD_FIRE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.FIRE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.DEAD_HORN_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.HORN_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.DEAD_TUBE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isWallCoral(Material.TUBE_CORAL_WALL_FAN)); - - Assertions.assertFalse(MaterialHelper.isWallCoral(Material.DEAD_TUBE_CORAL)); - Assertions.assertFalse(MaterialHelper.isWallCoral(Material.TUBE_CORAL)); - Assertions.assertFalse(MaterialHelper.isWallCoral(Material.TUBE_CORAL_BLOCK)); - } - - @Test - public void isButtonCompatibleTest() { - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.DEAD_BRAIN_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.BRAIN_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.DEAD_BUBBLE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.BUBBLE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.DEAD_FIRE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.FIRE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.DEAD_HORN_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.HORN_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.DEAD_TUBE_CORAL_WALL_FAN)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.TUBE_CORAL_WALL_FAN)); - - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.STONE_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.BIRCH_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.ACACIA_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.CRIMSON_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.OAK_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.DARK_OAK_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.JUNGLE_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.POLISHED_BLACKSTONE_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.SPRUCE_BUTTON)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.WARPED_BUTTON)); - - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.BLACK_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.RED_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.GREEN_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.BLUE_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.YELLOW_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.CYAN_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.LIME_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.BROWN_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.GRAY_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.LIGHT_BLUE_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.LIGHT_GRAY_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.MAGENTA_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.ORANGE_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.PINK_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.PURPLE_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.WHITE_SHULKER_BOX)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.SHULKER_BOX)); - - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.CHEST)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.ENDER_CHEST)); - Assertions.assertTrue(MaterialHelper.isButtonCompatible(Material.TRAPPED_CHEST)); - - //Chek something random to make sure isButtonCompatible is not just "return true;" - Assertions.assertFalse(MaterialHelper.isButtonCompatible(Material.OAK_LOG)); - } - -}