diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..5aad0a4ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.idea +.settings +.externalToolBuilders +*.iml +world +bin + +.classpath +.project +*.txt +*.bat \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..1d2fc3d48 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: java +jdk: oraclejdk7 +install: true +script: mvn -Dmaven.javadoc.skip=true clean install +notifications: + webhooks: + urls: + - http://api.getlwc.org/events/travisci + on_start: true +addons: + artifacts: + key: + secure: et4n0n8tn9ghMJ0g/tYDN063NYNDotu0JM02hVDUMupGWkwZWKGPgPNWpDnW3To8Fmg5SRLIqphfAnR5t0dyY/Yp6xHXgSzheUA+4376cH6lirrQMRkmZ0nvK909thiy9uNAvPO3vBdP+kEWtLWLqvjv/z9VYMsc4ELrflLZHsc= + secret: + secure: c95q4+B3zAftsvUpTNE80/Q48YPxBVh+Mc5ZJ5wIBQ2SOyPygTca6I4AVBmrL0TQBfjBGmka49MCdvA11aglVT9vPfWxWIT9qSbJmuHXFY5RlUG6wtHlTw3wLnjrAw6zE0RDzG+E2sEDi81ADe43kG/9xMuzYdPjvEpFT5LK5hM= + bucket: artifacts.getlwc.org + paths: + - $(git ls-files -x mcp -x original-*.jar -o *.jar *.zip | tr "\n" ":") diff --git a/COPYING b/COPYING deleted file mode 100644 index 3d90694ad..000000000 --- a/COPYING +++ /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/LICENSE b/LICENSE new file mode 100644 index 000000000..2ee6a7c55 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright 2011 Tyler Blair. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and contributors and should not be interpreted as representing official policies, +either expressed or implied, of anybody else. \ No newline at end of file diff --git a/README.md b/README.md index e78034260..a6979f075 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,14 @@ -#LWC, the Lightweight Chest Protection plugin +LWC +====== -Designed on top of hMod, It is fast and scalable. It does not use flat files, but instead sqlite -- a stateless, serverless SQL engine. In some cases it is actually faster than MySQL and other SQL engines. +*The* chest (and other blocks) protection plugin. -Features ----------------------- +Compilation +----------- -* Private chests by Users and Groups or even by Password -* Public chests that anyone can use, but no one can register -* Chest limits--limit the amount of chests a User or Group can register -* Small memory footprint - LWC uses both physical and memory databases and chests are only loaded into memory when they are needed until done with them -* No excessive disk I/O. The disk is only written to when a new Chest is protected, a limit created or an access right created (for private chests) -* Built in support for double chests: proper recognition only requires you to register one side of a double chest. Internally, only one chest is registered, saving storage space -* much more. some features are largely undocumented! +We use maven to handle our dependencies. -Modes ----------------------- -Modes are special features you can activate with commands to either make something easier to give you a useful tool or feature. They are not meant to be overpowered, and as a server admin you can disable any modes you don't want your server to be using. +* Install [Maven 3](http://maven.apache.org/download.html) +* Check out this repo and: `mvn clean package` -(todo) - -Converting ----------------------- - -Are you using ChestProtect? LWC can seamlessly convert every chest from Chest Protect 100% to LWC. If you use a different chest plugin, open a GitHub issue, and it could possibly be created. -To convert a chest, do one of the following: -In-game: `/lwc convert chestprotect` -Console: `java -cp plugins/LWC.jar CPConverter` - -Commands ----------------------- -You can view the latest commands by typing `/lwc` ingame, and then typing a command to view more information on it \ No newline at end of file +The core LWC plugin will be found inside `core/target/LWC.jar` \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..fdc669880 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +4.4.0 diff --git a/changelog.md b/changelog.md new file mode 100644 index 000000000..bf97d1c34 --- /dev/null +++ b/changelog.md @@ -0,0 +1,251 @@ +### 4.3.1 +# 4.3.1 +* Compatibility with Bukkit 1.4.4+. This retains compatibility with Bukkit 1.2.5+ +* **NEW**: Add config option for allowing / disallowing cross world drop transferring (disabled by default) +* **NEW**: Thanks to AlphA, add config support for blacklisting blocks that can be placed beside chests. As well, allow blacklisting specific players (e.g '[BuildCraft]') to block them from destroying protections +* **OPT**: Highly optimize magnet chests to be significantly more efficient. This fixes a lot of issues with lots of wasted queries being done with magnet chests +* **FIX**: IC2 wrenches could break LWC protected machines (MCPC) +* **FIX**: `/lwc admin reload` should reload cached configuration values +* **FIX**: `/lwc limits ` did not give helpful output when used in the console +* **FIX**: Send output of `/lwc limits ` to the right player +* **FIX**: Hide an underlying issue in the database core where shared prepared statements could cause deadlocks +* **FIX**: For the `StructureGrowEvent` event save CPU by not checking blocks that are not protectable +* **FIX**: Fix a potential (rare) dupe when using magnet chests +* **FIX**: For magnet chests, only pickup items that have lived for longer than their set pick up delay. Fixes instantly picking up items and also improves compatibility with plugins that want items to NOT be picked up. +* **FIX**: For magnet chests, do not place items into the output slots of Furnace slots. This fixes exploits related to EXP. This is mostly in part to a patch by Brianum (Thanks!) +* **FIX**: Misc bug fixes in other locations +* **FIX**: Run `/lwc admin cleanup` in a separate thread. While it will still slow the server down a lot, it will not freeze it and kick everyone off +* **FIX**: When Vault is being used, fallback to SuperPerms if Vault does not return any groups for a player (which is often a bug) +* **FIX**: Fix the `AUTOCLOSE` flag + +### 4.3.0 +# LWC 4.3.0 +* Stronger Tekkit/MCPC support - don't allow pipes to be placed adjacent to chests you cannot access. This prevents players from siphoning items out of your chest as long as it doesn't already have a pipe attached to it. +* Better compatibility with 1.3.1 +* `/lwc debug` will now show which groups LWC sees you as having +* Normalize startup output to look the same +* Automatic updating will now default to OFF +* **NEW**/worldguard: Tighter integration with WorldGuard thanks to **@bencvt** + * Adds the following types to `/cmodify` : `region:some_region` (or `r:some_region`) , `region:some_region:world` (or `r:some_region:world`) , and `region:#this` + * `r:some_region` will allow any member of `some_region` to open and access the protection + * `r:some_region:world` will allow any member of `some_region` (which is located in the world `world`) to open and access the protection + * `r:#this` simply delegates access to WorldGuard. If the protection is inside a region, **and** the player can **BUILD** where the chest is located at, they can open and access the protection +* **NEW**/limits: Allow support for item IDs in limit v2 definitions +* **NEW**/mobarena: Add Mob Arena. Don't allow Drop Transfer to be used inside Mob Arenas +* **BUG**/core: Don't change the "last accessed time" on protections if you are an LWC Admin and are not the owner of the protection +* **BUG**/core: Don't allow villagers to open protected doors. Can be changed in core.yml +* **BUG**/core: Don't download config files if one is needed, copy it from the jar file instead +* **BUG**/core: Don't allow tree growth (i.e from saplings) to destroy protected blocks +* **BUG**/doors: Play the 'door toggle' sound effect when LWC opens or closes a door +* **BUG**/magnet: Don't pickup items with an amount equal to or less than 0 +* **BUG**/modloader: Fix an issue with ModLoader items where some have a null material +* **BUG**/mcpc: Fix MCPC detection when using CB++ instead of CB + +### 4.2.1 +# LWC 4.2.1 +* Bug fix for vault + +### 4.2.0 +# LWC 4.2.0 +* **Significant** performance increases, in some cases upwards of 90%. In most cases, ProtectionInteract was >100k nanoseconds. It is now 20k-ish. The Redstone event, which accounted for 50% of the cpu time, at 20k nanoseconds/event, is now around 2k-3k nanoseconds per event. +* Vault Economy & Permissions support +* Added fence gates to the default configuration +* Less quiet exceptions when the updater fails for any reason +* CreeperHeal support - do not remove protections if creeper heal is activated & enabled :) +* Rewrote double door logic to be more reliable. It also does not reopen doors that are already closed. +* Work around a difficult to reproduce exploit that appears to be caused by a Bukkit bug, exact cause unknown (not on LWC's end, but it prevents it now) +* A block's protection history can be cycled through if you use the command `/lwc info history` + +### 4.1.1 +# LWC 4.1.1 +* Minor bug fix for metrics + +### 4.1.0 +# LWC 4.1.0 +* **R6 and Minecraft 1.2 support** +* **The following has been *COMPLETELY* REMOVED:** + * `/lwc schedule`; + * `/lwc menu`; + * `/lwc dev`; + * `/lwc admin config`; + * List support; + * EasyNotify; + * Job support (requirement for schedules); + * Kick traps; + * Ban traps; + * Deprecated `ProtectionTypes` class (use the Protection.Type enum instead!) +* Full LWC backups can be created using `/lwc admin backup create` -- this creates a full backup for the database and also every block along with it including any contents. + * Backups can be restored using `/lwc admin backup restore NAME` where NAME is the backup's file name without the extension (`.lwc` or `.lwc.gz`). The backups are stored in `plugins/LWC/backups/` + * At this time, the double chest side of double chests are not stored. +* Any /lwc admin protectregion NAME to protect all protectable LWC blocks in a WorldGuard region to 'LWCWorldGuard', the owner can be changed via /lwc admin updateprotections set owner = 'NewOwner' where owner ='LWCWorldGuard' (sorry for being so long spunk!) +* **Fix:** /cunlock could be used even after the correct password was used. + +### LWC 4.0.9 +* /lwc admin rebuild: a command to rebuild a wiped database, barring some missing data such as ACLs which is less important and unrecoverable without backups. See http://forums.bukkit.org/threads/60178/ + +### LWC 4.0.8 +* Fix an exploit that allows some very dangerous commands to be executed on the server + +### LWC 4.0.7 +* Maintenance release to fix an index. Most servers will not notice any difference. + +### LWC 4.0.6 +* bPermissions group support +* Allow the group prefix for matching groups via Permissions be changed in core.yml (and also change the default to group. instead of lwc.group.) +* Don't allow freshly placed pressure plates to open a door that has the redstone flag +* `/lwc admin purge` would interfere with LWC-Econ discount type IN_USE and not allow people to get discounted. + +### LWC 4.0.5 +* Fix gravity-matched blocks such as wooden sign, rails, lever, etc +* Prevent players from using /lwc fix on protections they cannot administrate + +### LWC 4.0.4 +* Incompatible classes when a method's return type was changed, so 4.0.4 is a complete rebuild of 4.0.3, fixing some bugs +* No source file changes except for plugin.yml + +### LWC 4.0.3 +* **New:** When removing one side of a protected double chest, it won't REMOVE the protection now. +* **New:** Support custom protections via the data value ( 'id:data' , e.g '181:8' ) + * This means Industrial Craft, etc blocks that use the data value for the block are now supported individually. +* Found another area where queries were being wasted when clicking on certain blocks +* Fixes an exploit detailed by `spunkiie` which allows you to arbitrarily take over certain protections +* Fix `plugins/LWC/locale/lwc.properties` - it was not being loaded properly +* Fix removal of double step protections when a step is placed and protected +* Fix automatic upgrade from sqlite->mysql +* Only perform rights migration from LWC3 -> LWC4 once even if the old rights table fails to drop +* Located and restored `/lwc admin purgeregion` - it went AWOL but has came back. +* Fail with a more descriptive failure when LWC cannot connect to the database at startup +* Fix an NPE in limitsv2 that is normally only found when using /climits +* soft defend iConomy to fix a bug when using the serverBankAccount feature of LWC-Economy +* Also soft depend Permissions & PermissionsBukkit again + * **NOTE:** If you use LWC-Economy and/or LWC-Spout, you may need to upgrade them as well. +* Group limits in limitsv2.yml are no longer case sensitive +* Fix the SQLite libraries being placed in the wrong location - should be `plugins/LWC/lib` not `plugins/LWC/lib/lib` :) +* Fix worldguard.allowProtectionsOutsideRegions: it was working in reverse (true meant it was blocked, false meant it was allowed) + +### LWC 4.0.2 +* Optimized out 1 to 2 unneeded queries that were used when touching a protected or unprotected door (for either they were two different things) +* Fix a bug where converted rights from LWC 3 were 'not able to access protections' +* Fix an exploit where a chest protection could be overridden by another protection in two very specific cases +* Minor additions to `/lwc credits` -- upgrade MonsterTKE and add imaxorz + +### LWC 4.0.1 +* Fix the german locale's file encoding (latin-1 -> utf-8) +* Fix a Spout bug (both in its RB and dev builds) that allow a sticky piston to destroy chests by pulling a wooden door over it +* Fix a bug in converting rights from LWC3 -> 4 format when the table is a bit tainted + +### LWC 4.0.0 +#### Licensing +* LWC 4 is now licensed under the **2-clause BSD license** + +#### Translations +* **New translation! Hungarian, courtesy of dretax** +* Most languages have been fully updated to the latest changes. + +#### Functionality +* The following commands have been added: (see the wiki for more in depth info) + * `/lwc history` + * `/lwc details` + * `/lwc schedule` + * `/lwc setup` + * `/lwc fix` + * New sub commands for `/lwc admin` +* Towny integration. You can now allow members of a specific Town access your Private protection, simply: `/cmodify t:TownName` or when creating it: `/cprivate t:TownName` +* The WorldGuard feature has been rewritten to be easier to use and now includes a blacklist feature, so you can blacklist specific regions from having protections. +* Add a new flag: `AUTOCLOSE` which makes a door automatically close after the configured amount of time in `plugins/LWC/doors.yml`. Usage: `/cautoclose on|off` +* Allow Fence Gates to be automatically closed by the autoclose flag & `doors.yml` +* Pressure plates will now be protected if they are placed in front of a door. Only players who have access to the protection can use the pressure plate. + * If you use `/credstone on` on the door, no redstone except the attached pressure plate will work on the door, meaning now only those who have access to the protection can open it. +* A protection's type can now be changed via `/cmodify type` e.g: `/cmodify private` will make a chest private. You can change a password (or change it TO password) via: `/cmodify password ThePassword` +* Limits V2. New limits system that is a lot easier to manage and use -- see `plugins/LWC/limitsv2.yml` Example commands: + * **`/climits` now fully functions as it should! Give the new updated version a shot!** + * `/lwc setup limits Hidendra 0` -- give hidendra 0 default protections + * `/lwc setup limits Hidendra default unlimited chest 5` -- give hidendra 5 chests and unlimited everything else + * `/lwc setup limits g:default 5` -- give the default group 5 protections + * `/lwc setup limits default 0` -- set the default amount of protections to 0 + * `/lwc setup limits hidendra 54 10 96 1` -- example of using block ids instead of names (10 chests, 1 trap door) +* add `quiet` under protections that can be set to a block that hides creation messages and notices + +#### Cosmetics +* `/lwc admin report` has been beautified +* `/cinfo` has been given a new look and is now more helpful. It will give you a shortlist of players who can access a private protection if you have appropriate access to it. + +#### Database +* Changes to the database format that make it not possible to downgrade from LWC4 to LWC3. However, LWC will still upgrade from 3 to 4 ok. +* All existing LWC 3 indexes have been wiped and LWC is now better indexed +* Startup time has been dramatically reduced (to nil) for those with a huge protection set +* More optimal caching techniques to ensure duplicate cache entries aren't present or created + +#### Internal +* The "bug 656 workaround" has been replaced with an automatic feature that does not need to be enabled, but is used when required +* `/lwc admin reload` will now also reload the loaded locale file (including the one in `plugins/LWC/locale/`) +* New & better updater. You can now subscribe to updates to the STABLE branch or BLEEDING_EDGE, which is the latest Jenkins builds. +* Locale messages defined as `null` will now not send the message to the player +* **FIX:** Protected blocks could be pulled with a sticky piston +* **FIX:** Doors could be destroyed by using a piston to push a dirt block towards it. +* **FIX:** Limits will now use the highest group limit instead of the first one found. +* **FIX:** The magnet module would sometimes not work as expected when used across multiple worlds. +* Metrics API for measuring plugin usage across the world using non-identifiable data. + * Developed by myself, all implementation specific details and backends are open source. + * View the data online: http://metrics.griefcraft.com/plugin/LWC + * Backend source: https://github.com/Hidendra/metrics.griefcraft.com +* `loadProtection(int id)` now utilizes the protection cache + +### 4.0.0-alpha7 +* Added `worldguard.allowProtectionsOutsideRegions` to the WorldGuard portion of LWC to decide if you want to allow protections outside of WorldGuard regions. +* Added `optional.onlyProtectWhenOwnerIsOffline` and `optional.onlyProtectWhenOwnerIsOnline` config options to core.yml. They allow protections to only be "active" (protecting items) when the owner of the protection is offline or online respectively. +* API: Added `removeProtectionsByPlayer` to PhysDB +* Added the commands: `/lwc admin updateprotections` `/lwc admin deleteprotections` `/lwc admin selectprotections` Delete/Select take a where argument and must be specified; e.g `/lwc admin deleteprotections world='World1'` and Update takes a Where and/or Set statement: `/lwc admin updateprotections set world='World2' where world='World1'` +* On the fly database conversion using `/lwc setup database NewType` and does not require plugin reloads or server restarts. E.g if you're on MySQL and want to go back to SQLite: `/lwc setup database sqlite` +* **FIX:** `/lwc admin report` would throw an exception +* **FIX:** Internal database version was not updating +* Startup time has been dramatically increased for those with a very large amount of protections. +* The bug 656 workaround has been replaced with a feature that is automatically used when chunk bug are detected. +* It is now **no longer possible (!!)** to easily downgrade to 3.5x without losing some data. + +### 4.0.0-alpha6 +* LWC is now licensed under the **2-clause BSD license**. All code prior to this point is still GPL, however. + +### 4.0.0-alpha5 +* Add `/lwc details ` which allows you to view specific information on a history item. +* Add `/lwc history` - used to view history items for a specific player or all latest ones. To be used hand in hand with `/lwc details` which views specific information on any given history item. The arguments for `/lwc history` are dynamic and as such, all of the following uses are valid: `/lwc history` `/lwc history *` `/lwc history 2` (page 2) `/lwc history 2 *` `/lwc history * 2` `/lwc history Hidendra` `/lwc history Hidendra` ... and so on! +* The way chests were facing were broken when upgrading from a pre-1.8 map to 1.8. The first time you click a protected chest you can access, it will reorient itself. Alternatively, you can use `/lwc fix` after this to manually orient a chest. +* **FIX:** Limits will now use the highest group limit instead of the first one found. +* **FIX:** The magnet module would sometimes not work as expected when used across multiple worlds. +* Added `t:TownName` and `town:TownName` to `/cmodify` and `/cprivate` for Towny integration. Allows residents of the town named to access that protection. +* Locale messages defined as `null` will now not send the message to the player +* New `AUTOCLOSE` flag, which allows doors to automatically close themselves after 3 seconds. Both sides of a double door must have this flag or doors may become out of sync. +* New LWC versions or builds can now be reloaded in without having to restart the server + +### 4.0.0-alpha4 +* `/lwc admin reload` will now also reload the loaded locale file (including the one in `plugins/LWC/locale/`) +* Added the `/lwc admin dump locale` utility - it dumps the current locale file into `plugins/LWC/locale/lwc.properties` +* Flags now support data. No utility to set data yet, however. +* The command `/lwc admin purgeregion [WorldName]` has been added to the WorldGuard module. If you are using it from the console, you must also specify the world that the region is in. If the region is in a different world than the player you use the command from, you must again also provide the world name. +* New updater + downloader. Currently, the default method of updating is via BLEEDING_EDGE. This is a build-by-build update and is currently set to manual by default. You can check your version with `/lwc admin version` and/or update to the latest with `/lwc admin update`. If you want automatic updating, in core.yml set core.updateMethod to: `updateMethod: AUTOMATIC` + +### 4.0.0-alpha3 +* **FIX:** Protected blocks could be pulled with a sticky piston +* **FIX:** Piston exploit that allowed protections such as Doors to be destroyed via: PISTON -> BLOCK -> PROTECTED DOOR +* Towny integration. Protections cannot be made outside of Towns, e.g the wild. Set `core.townyBorders` to true +* Double wooden doors will now function properly +* The openAndClose feature of double doors has been fixed +* Out of sync double doors can be fixed with the `/lwc fix` or `/lwc fixdoor` commands +* Fixes a duplication exploit related to the Magnet feature and the Showcase plugin +* The database is no longer used for LWC's in-memory database. It is now stored in-memory objects and even if the database connection is lost, LWC won't be totally unusable. +* Added the Expire job. It allows you to automatically expire protections - equivilent to `/lwc admin expire`. If you want to expire protections every week, that haven't been accessed in at least 2 weeks, do this: `/lwc schedule create test expire` `/lwc schedule arguments test 2 weeks` `/lwc schedule autoRun test 1 week` Done! If you want the block + inventory contents of the block to also be removed, you can add -remove after arguments, e.g: `/lwc schedule arguments test -remove 2 weeks` +* Added the Cleanup job. Allows you to automatically run `/lwc admin cleanup` +* Job scheduler. It allows you to manually run specific jobs or automatically run them at a given time (they can be provided by outside sources, too.) +* `/lwc schedule create JOBNAME TYPE` - Creates a job, for example: `/lwc schedule create weekly cleanup` +* `/lwc schedule run JOBNAME` - Manually run a job, for example: `/lwc schedule run weekly` +* `/lwc schedule check JOBNAME` - Look up when the job will next run, if it's to be automatically ran. It will tell you how long until the job runs. +* `/lwc schedule autorun JOBNAME TIME` - Schedule a job to automatically run. For example, run the weekly job every week: `/lwc schedule autorun weekly 1 week` - you can combine times, e.g: `/lwc schedule autorun weekly 2 days 12 hours` = every 60 hours +* `/lwc schedule list` - List all of the known jobs. YELLOW represents a job that is not automatically scheduled and must be automatically ran. GREEN represents a job that is automatically scheduled but is not a candidate to be ran yet. RED represents a job that is waiting to be ran. +* `/lwc schedule arguments JOBNAME ARGUMENTS` - for example: `/lwc create JOBNAME expire` , `/lwc schedule arguments JOBNAME -remove 2 weeks` makes the job JOBNAME which removes protections + blocks of protections that have not be accessed in at least 2 weeks. +* `/lwc tasks` can be used instead of `/lwc schedule` + +### 4.0.0-alpha2 +* `/lwc admin report` has had a makeover and now also shows cache read/writes +* Protection rights have been inlined with the main protections table +* Multi-group support has been added for Permissions 3.0+ +* Permissions support has been modified to always depend on Superperms, while LWC's own implementations will only be used for groups. This **breaks Permissions 2/3 support** unless you have SuperpermsBridge! diff --git a/core/lib/plugins/BOSEconomy.jar b/core/lib/plugins/BOSEconomy.jar new file mode 100644 index 000000000..c099feb88 Binary files /dev/null and b/core/lib/plugins/BOSEconomy.jar differ diff --git a/core/lib/plugins/CreeperHeal.jar b/core/lib/plugins/CreeperHeal.jar new file mode 100644 index 000000000..97a405684 Binary files /dev/null and b/core/lib/plugins/CreeperHeal.jar differ diff --git a/core/lib/plugins/Essentials.jar b/core/lib/plugins/Essentials.jar new file mode 100644 index 000000000..046f6e03d Binary files /dev/null and b/core/lib/plugins/Essentials.jar differ diff --git a/core/lib/plugins/Heroes.jar b/core/lib/plugins/Heroes.jar new file mode 100644 index 000000000..aab5d5e60 Binary files /dev/null and b/core/lib/plugins/Heroes.jar differ diff --git a/core/lib/plugins/MobArena-v0.94.4.52.jar b/core/lib/plugins/MobArena-v0.94.4.52.jar new file mode 100644 index 000000000..831ba6f34 Binary files /dev/null and b/core/lib/plugins/MobArena-v0.94.4.52.jar differ diff --git a/core/lib/plugins/PermissionsBukkit.jar b/core/lib/plugins/PermissionsBukkit.jar new file mode 100644 index 000000000..e1b323d01 Binary files /dev/null and b/core/lib/plugins/PermissionsBukkit.jar differ diff --git a/core/lib/plugins/PermissionsEx.jar b/core/lib/plugins/PermissionsEx.jar new file mode 100644 index 000000000..0ade30cf7 Binary files /dev/null and b/core/lib/plugins/PermissionsEx.jar differ diff --git a/core/lib/plugins/Showcase0.7.7.jar b/core/lib/plugins/Showcase0.7.7.jar new file mode 100644 index 000000000..64ed69f85 Binary files /dev/null and b/core/lib/plugins/Showcase0.7.7.jar differ diff --git a/core/lib/plugins/Towny.jar b/core/lib/plugins/Towny.jar new file mode 100644 index 000000000..870d55225 Binary files /dev/null and b/core/lib/plugins/Towny.jar differ diff --git a/core/lib/plugins/Towny_Advanced.jar b/core/lib/plugins/Towny_Advanced.jar new file mode 100644 index 000000000..9548820f0 Binary files /dev/null and b/core/lib/plugins/Towny_Advanced.jar differ diff --git a/core/lib/plugins/WorldEdit.jar b/core/lib/plugins/WorldEdit.jar new file mode 100644 index 000000000..c2e5ec7d3 Binary files /dev/null and b/core/lib/plugins/WorldEdit.jar differ diff --git a/core/lib/plugins/WorldGuard.jar b/core/lib/plugins/WorldGuard.jar new file mode 100644 index 000000000..683e559d1 Binary files /dev/null and b/core/lib/plugins/WorldGuard.jar differ diff --git a/core/lib/plugins/iConomy5.jar b/core/lib/plugins/iConomy5.jar new file mode 100644 index 000000000..0826420b2 Binary files /dev/null and b/core/lib/plugins/iConomy5.jar differ diff --git a/core/lib/plugins/iConomy6.jar b/core/lib/plugins/iConomy6.jar new file mode 100644 index 000000000..29cbd5197 Binary files /dev/null and b/core/lib/plugins/iConomy6.jar differ diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 000000000..d120a33e7 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,235 @@ + + 4.0.0 + com.griefcraft + lwc + LWC + getlwc.org + jar + 4.5.0-SNAPSHOT + + + UTF-8 + + + + + md-5 + http://repo.md-5.net/content/repositories/snapshots + + + + Plugin Metrics + http://repo.mcstats.org/content/repositories/public + + + + kitteh-repo + http://repo.kitteh.org/content/groups/public/ + + + + + + org.spigotmc + spigot-api + 1.7.10-R0.1-SNAPSHOT + provided + + + org.mcstats.bukkit + metrics + R8-SNAPSHOT + compile + + + junit + junit + 4.10 + test + + + + + net.milkbowl.vault + Vault + 1.2.27 + compile + + + de.bananaco + bPermissions + v2.12-DEV + + + BOSEconomy + BOSEconomy + 0.7.2 + system + ${project.basedir}/lib/plugins/BOSEconomy.jar + + + CreeperHeal + CreeperHeal + 5.8.5 + system + ${project.basedir}/lib/plugins/CreeperHeal.jar + + + Essentials + Essentials + 2.5.7 + system + ${project.basedir}/lib/plugins/Essentials.jar + + + Heroes + Heroes + 1.5.1-dev-b1666 + system + ${project.basedir}/lib/plugins/Heroes.jar + + + iConomy5 + iConomy5 + 5.01 + system + ${project.basedir}/lib/plugins/iConomy5.jar + + + iConomy6 + iConomy6 + 6.0 + system + ${project.basedir}/lib/plugins/iConomy6.jar + + + MobArena + MobArena + 0.94.4.52 + system + ${project.basedir}/lib/plugins/MobArena-v0.94.4.52.jar + + + PermissionsBukkit + PermissionsBukkit + 1.1 + system + ${project.basedir}/lib/plugins/PermissionsBukkit.jar + + + PermissionsEx + PermissionsEx + 1.13 + system + ${project.basedir}/lib/plugins/PermissionsEx.jar + + + Showcase + Showcase + 0.7.7 + system + ${project.basedir}/lib/plugins/Showcase0.7.7.jar + + + Towny + Towny + 0.74.0 + system + ${project.basedir}/lib/plugins/Towny.jar + + + Towny_Advanced + Towny_Advanced + 0.76.3.1 + system + ${project.basedir}/lib/plugins/Towny_Advanced.jar + + + WorldEdit + WorldEdit + 4.3 + system + ${project.basedir}/lib/plugins/WorldEdit.jar + + + WorldGuard + WorldGuard + 5.0-alpha8 + system + ${project.basedir}/lib/plugins/WorldGuard.jar + + + + + ${project.name} + clean package + + + + ${basedir}/src/main/resources + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.5 + 1.5 + + + + + org.apache.maven.plugins + maven-shade-plugin + 1.4 + + + package + + shade + + + + + org.mcstats.bukkit:metrics + + + + + org.mcstats + com.griefcraft.util.mcstats + + + + + + + + + maven-deploy-plugin + 2.7 + + false + + + + + + + + mcstats.releases + http://repo.mcstats.org/content/repositories/releases/ + + + + mcstats.snapshots + http://repo.mcstats.org/content/repositories/snapshots/ + + + + \ No newline at end of file diff --git a/core/src/main/java/com/griefcraft/cache/LRUCache.java b/core/src/main/java/com/griefcraft/cache/LRUCache.java new file mode 100644 index 000000000..10edcc10b --- /dev/null +++ b/core/src/main/java/com/griefcraft/cache/LRUCache.java @@ -0,0 +1,86 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.cache; + +import java.util.LinkedHashMap; + +public class LRUCache extends LinkedHashMap { + + /** + * The max number of entries allowed + */ + protected int maxCapacity; + + /** + * Amount of reads performed on the cache + */ + private long reads = 0; + + /** + * Amount of writes performed on the cache + */ + private long writes = 0; + + public LRUCache(int maxCapacity) { + super(maxCapacity, 0.75f, true); + this.maxCapacity = maxCapacity; + } + + @Override + public V get(Object key) { + reads++; + return super.get(key); + } + + @Override + public V put(K key, V value) { + writes++; + return super.put(key, value); + } + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + return size() > maxCapacity; + } + + /** + * @return amount of reads on the cache + */ + public long getReads() { + return reads; + } + + /** + * @return amount of writes on the cache + */ + public long getWrites() { + return writes; + } + +} diff --git a/core/src/main/java/com/griefcraft/cache/MethodCounter.java b/core/src/main/java/com/griefcraft/cache/MethodCounter.java new file mode 100644 index 000000000..2e82c1b83 --- /dev/null +++ b/core/src/main/java/com/griefcraft/cache/MethodCounter.java @@ -0,0 +1,104 @@ +package com.griefcraft.cache; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class MethodCounter { + + /** + * A map of the counts + */ + private final Map counts = new HashMap(); + + /** + * Increment a method in the counts + * + * @param method + */ + public void increment(String method) { + deltaMethod(method, 1); + } + + /** + * Decrement a method in the cache + * + * @param method + */ + public void decrement(String method) { + deltaMethod(method, -1); + } + + /** + * Get the counts for a method + * + * @param method + * @return + */ + public int get(String method) { + return counts.containsKey(method) ? counts.get(method) : 0; + } + + /** + * Sorts the method counts by the value and returns an unmodifiable map for it + * + * @return + */ + public Map sortByValue() { + return Collections.unmodifiableMap(sortByComparator(counts, false)); + } + + /** + * Handle increment / decrement + * + * @param method + * @param delta + */ + private void deltaMethod(String method, int delta) { + if (!counts.containsKey(method)) { + counts.put(method, delta); + return; + } + + counts.put(method, counts.get(method) + delta); + } + + /** + * Sort a hashmap by the values + *

+ * credits: http://stackoverflow.com/questions/8119366/sorting-hashmap-by-values/13913206#13913206 + * + * @param unsortMap + * @param order + * @return + */ + private static Map sortByComparator(Map unsortMap, final boolean order) { + List> list = new LinkedList>(unsortMap.entrySet()); + + // Sorting the list based on values + Collections.sort(list, new Comparator>() { + public int compare(Map.Entry o1, + Map.Entry o2) { + if (order) { + return o1.getValue().compareTo(o2.getValue()); + } else { + return o2.getValue().compareTo(o1.getValue()); + + } + } + }); + + // Maintaining insertion order with the help of LinkedList + Map sortedMap = new LinkedHashMap(); + for (Map.Entry entry : list) { + sortedMap.put(entry.getKey(), entry.getValue()); + } + + return sortedMap; + } + +} diff --git a/core/src/main/java/com/griefcraft/cache/ProtectionCache.java b/core/src/main/java/com/griefcraft/cache/ProtectionCache.java new file mode 100644 index 000000000..283f82942 --- /dev/null +++ b/core/src/main/java/com/griefcraft/cache/ProtectionCache.java @@ -0,0 +1,377 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.cache; + + +import com.griefcraft.lwc.LWC; +import com.griefcraft.model.Protection; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; + +public class ProtectionCache { + + /** + * The amount the cache is increased by each time a high-intensity area requests it if needed + */ + private final static int ADAPTIVE_CACHE_TICK = 10; + + /** + * The max number of protections the adaptive cache will add + */ + private final static int ADAPTIVE_CACHE_MAX = 100000; + + /** + * The LWC instance this set belongs to + */ + private final LWC lwc; + + /** + * Hard references to protections still cached + */ + private final LRUCache references; + + /** + * Weak references to protections and their cache key (protection.getCacheKey()) + */ + private final WeakLRUCache byCacheKey; + + /** + * Weak references to protections and their protection id + */ + private final WeakLRUCache byId; + + /** + * A block that isn't the protected block itself but matches it in a protection matcher + */ + private final WeakLRUCache byKnownBlock; + + /** + * A cache of blocks that are known to not have a protection + */ + private final LRUCache byKnownNulls; + + /** + * The capacity of the cache + */ + private int capacity; + + /** + * The number of protections that were added via adaptive cache + */ + private int adaptiveCapacity = 0; + + /** + * The method counter + */ + private final MethodCounter counter = new MethodCounter(); + + /** + * Used for byKnownNulls + */ + private final static Object FAKE_VALUE = new Object(); + + public ProtectionCache(LWC lwc) { + this.lwc = lwc; + this.capacity = lwc.getConfiguration().getInt("core.cacheSize", 10000); + + this.references = new LRUCache(capacity); + this.byCacheKey = new WeakLRUCache(capacity); + this.byId = new WeakLRUCache(capacity); + this.byKnownBlock = new WeakLRUCache(capacity); + this.byKnownNulls = new LRUCache(Math.min(10000, capacity)); // enforce a min size so we have a known buffer + } + + /** + * Called from specific potentially high-intensity access areas. These areas preferably need(!) free space in the + * cache and otherwise could cause "lag" or other oddities. + */ + public void increaseIfNecessary() { + if (isFull() && adaptiveCapacity < ADAPTIVE_CACHE_MAX) { + adaptiveCapacity += ADAPTIVE_CACHE_TICK; + adjustCacheSizes(); + } + } + + /** + * Gets the direct reference of the references cache + * + * @return + */ + public LRUCache getReferences() { + return references; + } + + /** + * Get the method counter for this class + * + * @return + */ + public MethodCounter getMethodCounter() { + return counter; + } + + /** + * Gets the default capacity of the cache + * + * @return + */ + public int capacity() { + return capacity; + } + + /** + * Gets the adaptive capacity of the cache + * + * @return + */ + public int adaptiveCapacity() { + return adaptiveCapacity; + } + + /** + * Gets the total capacity (default + adaptive) of the cache + * + * @return + */ + public int totalCapacity() { + return capacity + adaptiveCapacity; + } + + /** + * Clears the entire protection cache + */ + public void clear() { + // remove hard refs + references.clear(); + + // remove weak refs + byCacheKey.clear(); + byId.clear(); + byKnownBlock.clear(); + byKnownNulls.clear(); + } + + /** + * Check if the cache is full + * + * @return + */ + public boolean isFull() { + return references.size() >= totalCapacity(); + } + + /** + * Gets the amount of protections that are cached + * + * @return + */ + public int size() { + return references.size(); + } + + /** + * Cache a protection + * + * @param protection + */ + public void addProtection(Protection protection) { + if (protection == null) { + return; + } + + counter.increment("addProtection"); + + // Add the hard reference + references.put(protection, null); + + // Add weak references which are used to lookup protections + byCacheKey.put(protection.getCacheKey(), protection); + byId.put(protection.getId(), protection); + + // get the protection's finder if it was found via that + if (protection.getProtectionFinder() != null) { + Block protectedBlock = protection.getBlock(); + + for (BlockState state : protection.getProtectionFinder().getBlocks()) { + if (!protectedBlock.equals(state.getBlock())) { + String cacheKey = cacheKey(state.getLocation()); + byKnownBlock.put(cacheKey, protection); + } + } + } + } + + /** + * Remove the protection from the cache + * + * @param protection + */ + public void removeProtection(Protection protection) { + counter.increment("removeProtection"); + + references.remove(protection); + byId.remove(protection.getId()); + + if (protection.getProtectionFinder() != null) { + for (BlockState state : protection.getProtectionFinder().getBlocks()) { + remove(cacheKey(state.getLocation())); + } + } + } + + /** + * Remove the given cache key from any caches + * + * @param cacheKey + */ + public void remove(String cacheKey) { + byCacheKey.remove(cacheKey); + byKnownBlock.remove(cacheKey); + byKnownNulls.remove(cacheKey); + } + + /** + * Make a cache key known as null in the cache + * + * @param cacheKey + */ + public void addKnownNull(String cacheKey) { + counter.increment("addKnownNull"); + byKnownNulls.put(cacheKey, FAKE_VALUE); + } + + /** + * Check if a cache key is known to not exist in the database + * + * @param cacheKey + * @return + */ + public boolean isKnownNull(String cacheKey) { + counter.increment("isKnownNull"); + return byKnownNulls.containsKey(cacheKey); + } + + /** + * Get a protection in the cache via its cache key + * + * @param cacheKey + * @return + */ + public Protection getProtection(String cacheKey) { + counter.increment("getProtection"); + + Protection protection; + + // Check the direct cache first + if ((protection = byCacheKey.get(cacheKey)) != null) { + return protection; + } + + // now use the 'others' cache + return byKnownBlock.get(cacheKey); + } + + /** + * Get a protection in the cache located on the given block + * + * @param block + * @return + */ + public Protection getProtection(Block block) { + return getProtection(cacheKey(block.getWorld().getName(), block.getX(), block.getY(), block.getZ())); + } + + /** + * Get a protection in the cache located on the given block + * + * @param block + * @return + */ + public Protection getProtection(BlockState block) { + return getProtection(cacheKey(block.getWorld().getName(), block.getX(), block.getY(), block.getZ())); + } + + /** + * Check if the known block protection cache contains the given key + * + * @param block + * @return + */ + public boolean isKnownBlock(Block block) { + counter.increment("isKnownBlock"); + return byKnownBlock.containsKey(cacheKey(block.getWorld().getName(), block.getX(), block.getY(), block.getZ())); + } + + /** + * Get a protection in the cache via its id + * + * @param id + * @return + */ + public Protection getProtectionById(int id) { + counter.increment("getProtectionById"); + return byId.get(id); + } + + /** + * Gets the cache key for the given location + * + * @param location + * @return + */ + public String cacheKey(Location location) { + return cacheKey(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + /** + * Generate a cache key using the given data + * + * @param world + * @param x + * @param y + * @param z + * @return + */ + private String cacheKey(String world, int x, int y, int z) { + return world + ":" + x + ":" + y + ":" + z; + } + + /** + * Fixes the internal caches and adjusts them to the new cache total capacity + */ + private void adjustCacheSizes() { + references.maxCapacity = totalCapacity(); + byCacheKey.maxCapacity = totalCapacity(); + byId.maxCapacity = totalCapacity(); + byKnownBlock.maxCapacity = totalCapacity(); + byKnownNulls.maxCapacity = totalCapacity(); + } + +} diff --git a/core/src/main/java/com/griefcraft/cache/WeakLRUCache.java b/core/src/main/java/com/griefcraft/cache/WeakLRUCache.java new file mode 100644 index 000000000..c668a9c9b --- /dev/null +++ b/core/src/main/java/com/griefcraft/cache/WeakLRUCache.java @@ -0,0 +1,205 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.cache; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * Similar to LRUCache but instead uses WeakReferences. + * The key must be a hard ref, while the value will be a weak reference + */ +public class WeakLRUCache implements Map { + + /** + * The backing linked hashmap for the cache + */ + private final LinkedHashMap> weakCache; + + /** + * The queue of references to be removed + */ + private final ReferenceQueue queue = new ReferenceQueue(); + + /** + * The cache's max capacity + */ + protected int maxCapacity; + + /** + * Amount of reads performed on the cache + */ + private long reads = 0; + + /** + * Amount of writes performed on the cache + */ + private long writes = 0; + + public WeakLRUCache(int capacity) { + this.maxCapacity = capacity; + + this.weakCache = new LinkedHashMap>(maxCapacity) { + @Override + protected boolean removeEldestEntry(java.util.Map.Entry> eldest) { + return size() > maxCapacity; + } + }; + } + + /** + * @return amount of reads on the cache + */ + public long getReads() { + return reads; + } + + /** + * @return amount of writes on the cache + */ + public long getWrites() { + return writes; + } + + /** + * Processes the reference queue and removes any garbage collected values + */ + private void processQueue() { + WeakValue weakValue; + while ((weakValue = (WeakValue) queue.poll()) != null) { + // remove it from the cache + weakCache.remove(weakValue.key); + } + } + + /** + * Gets the size of the cache currently + * + * @return + */ + public int size() { + processQueue(); + return weakCache.size(); + } + + public boolean isEmpty() { + processQueue(); + return weakCache.isEmpty(); + } + + public boolean containsKey(Object key) { + processQueue(); + return weakCache.containsKey(key); + } + + public boolean containsValue(Object value) { + processQueue(); + return weakCache.containsValue(value); + } + + public void clear() { + processQueue(); + weakCache.clear(); + } + + public Set keySet() { + processQueue(); + return weakCache.keySet(); + } + + public V get(Object key) { + reads++; + processQueue(); + + WeakValue weakRef = weakCache.get(key); + V result = null; + + if (weakRef != null) { + result = weakRef.get(); + + // If the result is still null, we should remove it! + if (result == null) { + weakCache.remove(key); + } + } + + return result; + } + + public V put(K key, V value) { + writes++; + processQueue(); + + WeakValue oldRef = weakCache.put(key, new WeakValue(value, key, queue)); + return oldRef != null ? oldRef.get() : null; + } + + public V remove(Object key) { + WeakValue old = weakCache.remove(key); + return old != null ? old.get() : null; + } + + public void putAll(Map m) { + throw new UnsupportedOperationException("putAll() is not supported by WeakLRUCache"); + } + + public Collection values() { + throw new UnsupportedOperationException("values() is not supported by WeakLRUCache"); + } + + public Set> entrySet() { + throw new UnsupportedOperationException("entrySet() is not supported by WeakLRUCache"); + } + + /** + * A WeakValue which stores the value of the weak reference and the key in the HashMap + * so it can be more quickly removed when GCd + * + * @param + * @param + */ + private final class WeakValue extends WeakReference { + + /** + * The key for the value + */ + private final K key; + + private WeakValue(V value, K key, ReferenceQueue queue) { + super(value, queue); + this.key = key; + } + + } + +} diff --git a/core/src/main/java/com/griefcraft/integration/ICurrency.java b/core/src/main/java/com/griefcraft/integration/ICurrency.java new file mode 100644 index 000000000..4e163c92a --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/ICurrency.java @@ -0,0 +1,106 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration; + +import org.bukkit.entity.Player; + +public interface ICurrency { + + /** + * @return true if a currency is active + */ + public boolean isActive(); + + /** + * @return true if the Economy plugin can support the server account feature + */ + public boolean usingCentralBank(); + + /** + * Format money + * + * @param money + * @return + */ + public String format(double money); + + /** + * Get the money name (e.g dollars) + * + * @return + */ + public String getMoneyName(); + + /** + * Get the current balance for a player + * + * @param player + * @return + */ + public double getBalance(Player player); + + /** + * Check the player's money to see if they can afford that + * amount of money without going negative. + * + * @param player + * @param money + * @return + */ + public boolean canAfford(Player player, double money); + + /** + * Check if the server account can afford the amount of money given + * + * @param money + * @return true if the server account has a balance equal or greater to the money given + */ + public boolean canCentralBankAfford(double money); + + /** + * Add money to a player's bank account + * If server account banking is enabled, the money is automatically withdrawn from the configured bank! + * + * @param player + * @param money + * @return the balance after modifying the player's account + */ + public double addMoney(Player player, double money); + + /** + * Remove money from a player's bank account + * If server account banking is enabled, the money is automatically added to the configured bank! + * + * @param player + * @param money + * @return the balance after modifying the player's account + */ + public double removeMoney(Player player, double money); + +} diff --git a/core/src/main/java/com/griefcraft/integration/IPermissions.java b/core/src/main/java/com/griefcraft/integration/IPermissions.java new file mode 100644 index 000000000..ae452aeb8 --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/IPermissions.java @@ -0,0 +1,45 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration; + +import org.bukkit.entity.Player; + +import java.util.List; + +public interface IPermissions { + + /** + * Get the groups a player belongs to + * + * @param player + * @return + */ + public List getGroups(Player player); + +} diff --git a/core/src/main/java/com/griefcraft/integration/currency/BOSECurrency.java b/core/src/main/java/com/griefcraft/integration/currency/BOSECurrency.java new file mode 100644 index 000000000..a69949fc9 --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/currency/BOSECurrency.java @@ -0,0 +1,132 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.currency; + +import com.griefcraft.integration.ICurrency; +import com.griefcraft.util.config.Configuration; +import cosine.boseconomy.BOSEconomy; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class BOSECurrency implements ICurrency { + + /** + * The BOSEconomy plugin instance + */ + private BOSEconomy handler; + + /** + * The economy configuration + */ + private Configuration configuration = Configuration.load("iconomy.yml"); + + /** + * The server account to use + */ + private String serverAccount; + + public BOSECurrency() { + handler = (BOSEconomy) Bukkit.getServer().getPluginManager().getPlugin("BOSEconomy"); + serverAccount = configuration.getString("iConomy.serverBankAccount", ""); + + // create the bank if it does not exist + if (!serverAccount.isEmpty() && !handler.bankExists(serverAccount)) { + handler.createBank(serverAccount); + } + } + + public boolean isActive() { + return true; + } + + public boolean usingCentralBank() { + return !serverAccount.isEmpty(); + } + + public String format(double money) { + return handler.getMoneyFormatted(money); + } + + public String getMoneyName() { + return handler.getMoneyName(); + } + + public double getBalance(Player player) { + if (player == null) { + return 0; + } + + return handler.getPlayerMoneyDouble(player.getName()); + } + + public boolean canAfford(Player player, double money) { + return player != null && getBalance(player) >= money; + + } + + public boolean canCentralBankAfford(double money) { + return handler.getBankMoneyDouble(serverAccount) >= money; + } + + public double addMoney(Player player, double money) { + if (player == null) { + return 0; + } + + // remove the money from the central bank if applicable + if (usingCentralBank()) { + if (!canCentralBankAfford(money)) { + return 0; + } + + handler.addBankMoney(serverAccount, -money, true); + } + + handler.addPlayerMoney(player.getName(), money, false); + + return getBalance(player); + } + + public double removeMoney(Player player, double money) { + // we're removing money, so it should be positive + if (money > 0) { + money = -money; + } + + // add the money to the central bank if applicable + if (usingCentralBank()) { + handler.addBankMoney(serverAccount, money, true); + } + + handler.addPlayerMoney(player.getName(), money, false); + + return getBalance(player); + } + +} diff --git a/core/src/main/java/com/griefcraft/integration/currency/EssentialsCurrency.java b/core/src/main/java/com/griefcraft/integration/currency/EssentialsCurrency.java new file mode 100644 index 000000000..38a8226d0 --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/currency/EssentialsCurrency.java @@ -0,0 +1,129 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.currency; + +import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.api.Economy; +import com.earth2me.essentials.api.NoLoanPermittedException; +import com.earth2me.essentials.api.UserDoesNotExistException; +import com.griefcraft.integration.ICurrency; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class EssentialsCurrency implements ICurrency { + + /** + * The Essentials plugin object + */ + private Essentials essentials; + + public EssentialsCurrency() { + Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin("Essentials"); + + if (plugin == null) { + return; + } + + essentials = (Essentials) plugin; + } + + public boolean isActive() { + return true; + } + + public boolean usingCentralBank() { + return false; + } + + public String format(double money) { + return Economy.format(money); + } + + public String getMoneyName() { + return essentials.getSettings().getCurrencySymbol(); + } + + public double getBalance(Player player) { + if (player == null) { + return 0; + } + + try { + return Economy.getMoney(player.getName()); + } catch (UserDoesNotExistException e) { + return 0d; + } + } + + public boolean canAfford(Player player, double money) { + if (player == null) { + return false; + } + + try { + return Economy.hasEnough(player.getName(), money); + } catch (UserDoesNotExistException e) { + return false; + } + } + + public boolean canCentralBankAfford(double money) { + return false; + } + + public double addMoney(Player player, double money) { + if (player == null) { + return 0; + } + + try { + Economy.add(player.getName(), money); + } catch (UserDoesNotExistException e) { + return 0; + } catch (NoLoanPermittedException e) { + return 0; + } + + return getBalance(player); + } + + public double removeMoney(Player player, double money) { + try { + Economy.subtract(player.getName(), money); + } catch (UserDoesNotExistException e) { + return 0; + } catch (NoLoanPermittedException e) { + return 0; + } + + return getBalance(player); + } + +} diff --git a/core/src/main/java/com/griefcraft/integration/currency/NoCurrency.java b/core/src/main/java/com/griefcraft/integration/currency/NoCurrency.java new file mode 100644 index 000000000..fd554252c --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/currency/NoCurrency.java @@ -0,0 +1,72 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.currency; + +import com.griefcraft.integration.ICurrency; +import org.bukkit.entity.Player; + +public class NoCurrency implements ICurrency { + + public boolean isActive() { + return false; + } + + public boolean usingCentralBank() { + return false; + } + + public String format(double money) { + return Double.toString(money); + } + + public String getMoneyName() { + return ""; + } + + public double getBalance(Player player) { + return 0; + } + + public boolean canAfford(Player player, double money) { + return false; + } + + public boolean canCentralBankAfford(double money) { + return false; + } + + public double addMoney(Player player, double money) { + return 0; + } + + public double removeMoney(Player player, double money) { + return 0; + } + +} diff --git a/core/src/main/java/com/griefcraft/integration/currency/VaultCurrency.java b/core/src/main/java/com/griefcraft/integration/currency/VaultCurrency.java new file mode 100644 index 000000000..c41edd14a --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/currency/VaultCurrency.java @@ -0,0 +1,103 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.currency; + +import com.griefcraft.integration.ICurrency; +import net.milkbowl.vault.economy.Economy; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; + +public class VaultCurrency implements ICurrency { + + /** + * The economy services handler + */ + private Economy economy; + + public VaultCurrency() { + checkEconomy(); + } + + /** + * Check for an economy providor and set it if it was found + */ + private void checkEconomy() { + RegisteredServiceProvider serviceProvider = Bukkit.getServer().getServicesManager().getRegistration(Economy.class); + + if (serviceProvider != null) { + economy = serviceProvider.getProvider(); + } + } + + public boolean isActive() { + // If the economy provider is still null it is possible it hasn't been hooked in yet + // So we just check for it again, since isActive() is called before any econ + // calls should be used :D + if (economy == null) { + checkEconomy(); + } + + return economy != null; + } + + public boolean usingCentralBank() { + return false; + } + + public String format(double money) { + return economy.format(money); + } + + public String getMoneyName() { + return economy.currencyNameSingular(); + } + + public double getBalance(Player player) { + return economy.getBalance(player.getName()); + } + + public boolean canAfford(Player player, double money) { + return economy.has(player.getName(), money); + } + + public boolean canCentralBankAfford(double money) { + return false; + } + + public double addMoney(Player player, double money) { + economy.depositPlayer(player.getName(), money); + return getBalance(player); + } + + public double removeMoney(Player player, double money) { + economy.withdrawPlayer(player.getName(), money); + return getBalance(player); + } +} diff --git a/core/src/main/java/com/griefcraft/integration/currency/iConomy5Currency.java b/core/src/main/java/com/griefcraft/integration/currency/iConomy5Currency.java new file mode 100644 index 000000000..c7530f87f --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/currency/iConomy5Currency.java @@ -0,0 +1,181 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.currency; + +import com.griefcraft.integration.ICurrency; +import com.griefcraft.util.config.Configuration; +import com.iConomy.iConomy; +import com.iConomy.system.Account; +import com.iConomy.system.Holdings; +import com.iConomy.util.Constants; +import org.bukkit.entity.Player; + +public class iConomy5Currency implements ICurrency { + + /** + * The economy configuration + */ + private Configuration configuration = Configuration.load("iconomy.yml"); + + /** + * The server account to use + */ + private String serverAccount; + + public iConomy5Currency() { + serverAccount = configuration.getString("iConomy.serverBankAccount", ""); + + // create the account in iConomy if needed + if (!serverAccount.isEmpty()) { + iConomy.getAccount(serverAccount); + } + } + + public boolean isActive() { + return true; + } + + public boolean usingCentralBank() { + return !serverAccount.isEmpty(); + } + + public String format(double money) { + return iConomy.format(money); + } + + public String getMoneyName() { + return Constants.Major.get(1); + } + + public double getBalance(Player player) { + if (player == null) { + return 0; + } + + Account account = iConomy.getAccount(player.getName()); + + if (account == null) { + return 0; + } + + return account.getHoldings().balance(); + } + + public boolean canAfford(Player player, double money) { + if (player == null) { + return false; + } + + Account account = iConomy.getAccount(player.getName()); + + return account != null && account.getHoldings().hasEnough(money); + } + + public boolean canCentralBankAfford(double money) { + if (!usingCentralBank()) { + return true; + } + + Account account = iConomy.getAccount(serverAccount); + + return account != null && account.getHoldings().hasEnough(money); + } + + public double addMoney(Player player, double money) { + if (player == null) { + return 0; + } + + // remove the money from the central bank if applicable + if (usingCentralBank()) { + if (!canCentralBankAfford(money)) { + return 0; + } + + Account central = iConomy.getAccount(serverAccount); + + if (central == null) { + return 0; + } + + central.getHoldings().subtract(money); + } + + Account account = iConomy.getAccount(player.getName()); + + if (account == null) { + return 0; + } + + Holdings holdings = account.getHoldings(); + holdings.add(money); + + return holdings.balance(); + } + + public double removeMoney(Player player, double money) { + if (player == null) { + return 0; + } + + // we're removing money, so it should be positive + if (money < 0) { + money = -money; + } + + // add the money to the central bank if applicable + if (usingCentralBank()) { + Account central = iConomy.getAccount(serverAccount); + + if (central == null) { + return 0; + } + + central.getHoldings().add(money); + } + + Account account = iConomy.getAccount(player.getName()); + + if (account == null) { + return 0; + } + + Holdings holdings = account.getHoldings(); + + // this SHOULD be a transaction, ensure they have enough + if (!holdings.hasEnough(money)) { + return holdings.balance(); + } + + holdings.subtract(money); + + return holdings.balance(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/griefcraft/integration/currency/iConomy6Currency.java b/core/src/main/java/com/griefcraft/integration/currency/iConomy6Currency.java new file mode 100644 index 000000000..47fd96804 --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/currency/iConomy6Currency.java @@ -0,0 +1,187 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.currency; + +import com.griefcraft.integration.ICurrency; +import com.griefcraft.util.config.Configuration; +import com.iCo6.Constants; +import com.iCo6.iConomy; +import com.iCo6.system.Account; +import com.iCo6.system.Accounts; +import com.iCo6.system.Holdings; +import org.bukkit.entity.Player; + +public class iConomy6Currency implements ICurrency { + + /** + * The object used to obtain accounts + */ + private final Accounts accounts = new Accounts(); + + /** + * The economy configuration + */ + private Configuration configuration = Configuration.load("iconomy.yml"); + + /** + * The server account to use + */ + private String serverAccount; + + public iConomy6Currency() { + serverAccount = configuration.getString("iConomy.serverBankAccount", ""); + + // create the account in iConomy if needed + if (!serverAccount.isEmpty()) { + accounts.get(serverAccount); + } + } + + public boolean isActive() { + return true; + } + + public boolean usingCentralBank() { + return !serverAccount.isEmpty(); + } + + public String format(double money) { + return iConomy.format(money); + } + + public String getMoneyName() { + return Constants.Nodes.Major.getStringList().get(1); + } + + public double getBalance(Player player) { + if (player == null) { + return 0; + } + + Account account = accounts.get(player.getName()); + + if (account == null) { + return 0; + } + + return account.getHoldings().getBalance(); + } + + public boolean canAfford(Player player, double money) { + if (player == null) { + return false; + } + + Account account = accounts.get(player.getName()); + + return account != null && account.getHoldings().hasEnough(money); + } + + public boolean canCentralBankAfford(double money) { + if (!usingCentralBank()) { + return true; + } + + Account account = accounts.get(serverAccount); + + return account != null && account.getHoldings().hasEnough(money); + } + + public double addMoney(Player player, double money) { + if (player == null) { + return 0; + } + + // remove the money from the central bank if applicable + if (usingCentralBank()) { + if (!canCentralBankAfford(money)) { + return 0; + } + + Account central = accounts.get(serverAccount); + + if (central == null) { + return 0; + } + + central.getHoldings().subtract(money); + } + + Account account = accounts.get(player.getName()); + + if (account == null) { + return 0; + } + + Holdings holdings = account.getHoldings(); + holdings.add(money); + + return holdings.getBalance(); + } + + public double removeMoney(Player player, double money) { + if (player == null) { + return 0; + } + + // we're removing money, so it should be positive + if (money < 0) { + money = -money; + } + + // add the money to the central bank if applicable + if (usingCentralBank()) { + Account central = accounts.get(serverAccount); + + if (central == null) { + return 0; + } + + central.getHoldings().add(money); + } + + Account account = accounts.get(player.getName()); + + if (account == null) { + return 0; + } + + Holdings holdings = account.getHoldings(); + + // this SHOULD be a transaction, ensure they have enough + if (!holdings.hasEnough(money)) { + return holdings.getBalance(); + } + + holdings.subtract(money); + + return holdings.getBalance(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/griefcraft/integration/permissions/BukkitPermissions.java b/core/src/main/java/com/griefcraft/integration/permissions/BukkitPermissions.java new file mode 100644 index 000000000..1de07827b --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/permissions/BukkitPermissions.java @@ -0,0 +1,80 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.permissions; + +import com.platymuus.bukkit.permissions.Group; +import com.platymuus.bukkit.permissions.PermissionsPlugin; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.util.List; + +/** + * BukkitPermissions is supported by the CraftBukkit Recommended Build #1000+ ONLY + */ +public class BukkitPermissions extends SuperPermsPermissions { + + /** + * The PermissionsBukkit handler + */ + private PermissionsPlugin handler = null; + + public BukkitPermissions() { + Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin("PermissionsBukkit"); + + if (plugin == null) { + return; + } + + handler = (PermissionsPlugin) plugin; + } + + public List getGroups(Player player) { + List groups = super.getGroups(player); + + if (handler == null) { + return groups; + } + + List found = handler.getGroups(player.getName()); + + if (found.size() == 0) { + return groups; + } + + // add in the groups + for (Group group : found) { + groups.add(group.getName()); + } + + return groups; + } + +} diff --git a/core/src/main/java/com/griefcraft/integration/permissions/PEXPermissions.java b/core/src/main/java/com/griefcraft/integration/permissions/PEXPermissions.java new file mode 100644 index 000000000..119ad37d5 --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/permissions/PEXPermissions.java @@ -0,0 +1,51 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.permissions; + +import org.bukkit.entity.Player; +import ru.tehkode.permissions.PermissionUser; +import ru.tehkode.permissions.bukkit.PermissionsEx; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PEXPermissions extends SuperPermsPermissions { + + public List getGroups(Player player) { + PermissionUser user = PermissionsEx.getPermissionManager().getUser(player); + + if (user == null) { + return new ArrayList(); + } + + return Arrays.asList(user.getGroupsNames()); + } + +} diff --git a/core/src/main/java/com/griefcraft/integration/permissions/SuperPermsPermissions.java b/core/src/main/java/com/griefcraft/integration/permissions/SuperPermsPermissions.java new file mode 100644 index 000000000..b348a6ef4 --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/permissions/SuperPermsPermissions.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.permissions; + +import com.griefcraft.integration.IPermissions; +import com.griefcraft.lwc.LWC; +import org.bukkit.entity.Player; +import org.bukkit.permissions.PermissionAttachmentInfo; + +import java.util.ArrayList; +import java.util.List; + +public class SuperPermsPermissions implements IPermissions { + + /** + * The group prefix to use to lookup in Permissions - can be overrided in core.yml with groupPrefix: 'new.prefix.' + * Must include leading period (.) + *

+ * Default: lwc.group. + */ + private String groupPrefix; + + public SuperPermsPermissions() { + groupPrefix = LWC.getInstance().getConfiguration().getString("core.groupPrefix", "group."); + } + + // modified implementation by ZerothAngel ( https://github.com/Hidendra/LWC/issues/88#issuecomment-2017807 ) + public List getGroups(Player player) { + LWC lwc = LWC.getInstance(); + List groups = new ArrayList(); + + for (PermissionAttachmentInfo pai : player.getEffectivePermissions()) { + if (pai.getPermission().startsWith(groupPrefix)) { + groups.add(pai.getPermission().substring(groupPrefix.length())); + } + } + + return groups; + } + +} diff --git a/core/src/main/java/com/griefcraft/integration/permissions/VaultPermissions.java b/core/src/main/java/com/griefcraft/integration/permissions/VaultPermissions.java new file mode 100644 index 000000000..fa417f5ea --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/permissions/VaultPermissions.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.permissions; + +import net.milkbowl.vault.permission.Permission; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; + +import java.util.Arrays; +import java.util.List; + +public class VaultPermissions extends SuperPermsPermissions { + + @Override + public List getGroups(Player player) { + RegisteredServiceProvider serviceProvider = Bukkit.getServer().getServicesManager().getRegistration(Permission.class); + + if (serviceProvider == null) { + return super.getGroups(player); + } + + Permission perm = serviceProvider.getProvider(); + + try { + // the player's groups + String[] groups = perm.getPlayerGroups(player); + + // fallback to superperms if it appears that they have no groups + if (groups == null || groups.length == 0) { + return super.getGroups(player); + } + + return Arrays.asList(groups); + } catch (UnsupportedOperationException e) { + // Can be thrown by vault or asList. Thrown by Vault when using SuperPerms - getPlayerGroups() will throw it :-( + return super.getGroups(player); + } + } + +} diff --git a/core/src/main/java/com/griefcraft/integration/permissions/bPermissions.java b/core/src/main/java/com/griefcraft/integration/permissions/bPermissions.java new file mode 100644 index 000000000..9bf628c19 --- /dev/null +++ b/core/src/main/java/com/griefcraft/integration/permissions/bPermissions.java @@ -0,0 +1,68 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.integration.permissions; + +import de.bananaco.bpermissions.api.User; +import de.bananaco.bpermissions.api.World; +import de.bananaco.bpermissions.api.WorldManager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.util.ArrayList; +import java.util.List; + +public class bPermissions extends SuperPermsPermissions { + + /** + * bPermissions permission handler + */ + private WorldManager handler; + + public bPermissions() { + Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin("bPermissions"); + + if (plugin == null) { + return; + } + + handler = WorldManager.getInstance(); + } + + public List getGroups(Player player) { + // load the permission world + World world = handler.getWorld(player.getWorld().getName()); + + // Get the user + User user = world.getUser(player.getName()); + + return new ArrayList(user.getGroupsAsString()); + } + +} diff --git a/core/src/main/java/com/griefcraft/io/Backup.java b/core/src/main/java/com/griefcraft/io/Backup.java new file mode 100644 index 000000000..0b24c9a31 --- /dev/null +++ b/core/src/main/java/com/griefcraft/io/Backup.java @@ -0,0 +1,280 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.io; + +import org.bukkit.inventory.ItemStack; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.EnumSet; +import java.util.Map; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class Backup { + + /** + * The backup file's current revision + */ + public static final int CURRENT_REVISION = 1; + + /** + * The operations the backup is allowed to perform + */ + public enum OperationMode { + READ, WRITE + } + + /** + * The file this backup is located at + */ + private final File file; + + /** + * The operationMode we are allowed to perform + */ + private final OperationMode operationMode; + + /** + * The flags we are using + */ + private final EnumSet flags; + + /** + * This backup's revision number + */ + private int revision; + + /** + * The time the backup was created it + */ + private long created; + + /** + * The backup's input stream if we are reading + */ + private DataInputStream inputStream; + + /** + * The backup's output stream if we are writing + */ + private DataOutputStream outputStream; + + public Backup(File file, OperationMode operationMode, EnumSet flags) throws IOException { + this.file = file; + this.operationMode = operationMode; + this.flags = flags; + + if (!file.exists()) { + if (operationMode == OperationMode.READ) { + throw new UnsupportedOperationException("The backup could not be read"); + } else { + file.createNewFile(); + } + } + + // Set some base data if we're writing + if (operationMode == OperationMode.WRITE) { + revision = CURRENT_REVISION; + created = System.currentTimeMillis() / 1000; + } + + // Are we using compression? + boolean compression = flags.contains(BackupManager.Flag.COMPRESSION); + + // create the stream we need + if (operationMode == OperationMode.READ) { + FileInputStream fis = new FileInputStream(file); + inputStream = new DataInputStream(compression ? new GZIPInputStream(fis) : fis); + } else if (operationMode == OperationMode.WRITE) { + FileOutputStream fos = new FileOutputStream(file); + outputStream = new DataOutputStream(compression ? new GZIPOutputStream(fos) : fos); + } + } + + /** + * Read an entity from the backup file + * + * @return + */ + protected Restorable readRestorable() throws IOException { + if (operationMode != OperationMode.READ) { + throw new UnsupportedOperationException("READ is not allowed on this backup."); + } + + // The object type + int type = (byte) inputStream.read(); + + // EOF + if (type == -1) { + return null; + } + + // TODO enum that shit yo + if (type == 0) { // Protection + RestorableProtection rprotection = new RestorableProtection(); + rprotection.setId(inputStream.readInt()); + rprotection.setProtectionType(inputStream.readByte()); + rprotection.setBlockId(inputStream.readShort()); + rprotection.setOwner(inputStream.readUTF()); + rprotection.setWorld(inputStream.readUTF()); + rprotection.setX(inputStream.readInt()); + rprotection.setY(inputStream.readShort()); + rprotection.setZ(inputStream.readInt()); + rprotection.setData(inputStream.readUTF()); + rprotection.setCreated(inputStream.readLong()); + rprotection.setUpdated(inputStream.readLong()); + + return rprotection; + } else if (type == 1) { // Block + RestorableBlock rblock = new RestorableBlock(); + rblock.setId(inputStream.readShort()); + rblock.setWorld(inputStream.readUTF()); + rblock.setX(inputStream.readInt()); + rblock.setY(inputStream.readShort()); + rblock.setZ(inputStream.readInt()); + rblock.setData(inputStream.read() & 0xFF); + int itemCount = inputStream.readShort(); + + for (int i = 0; i < itemCount; i++) { + // Read in us some RestorableItems + int slot = inputStream.readShort(); + int itemId = inputStream.readShort(); + int amount = inputStream.readShort(); + short damage = inputStream.readShort(); + + // Create the stack + ItemStack itemStack = new ItemStack(itemId, amount, damage); + + // add it to the block + rblock.setSlot(slot, itemStack); + } + + // Woo! + return rblock; + } + + throw new UnsupportedOperationException("Read unknown type: " + type); + } + + /** + * Write an entity to the backup file + * + * @param restorable + */ + protected void writeRestorable(Restorable restorable) throws IOException { + if (operationMode != OperationMode.WRITE) { + throw new UnsupportedOperationException("WRITE is not allowed on this backup."); + } + + // write the id + outputStream.write((byte) restorable.getType()); + + // Write it + if (restorable.getType() == 0) { // Protection, also TODO ENUMSSSSSSSSSSS + RestorableProtection rprotection = (RestorableProtection) restorable; + + outputStream.writeInt(rprotection.getId()); + outputStream.writeByte(rprotection.getType()); + outputStream.writeShort(rprotection.getBlockId()); + outputStream.writeUTF(rprotection.getOwner()); + outputStream.writeUTF(rprotection.getWorld()); + outputStream.writeInt(rprotection.getX()); + outputStream.writeShort(rprotection.getY()); + outputStream.writeInt(rprotection.getZ()); + outputStream.writeUTF(rprotection.getData()); + outputStream.writeLong(rprotection.getCreated()); + outputStream.writeLong(rprotection.getUpdated()); + } else if (restorable.getType() == 1) { // Block, TODO DID I SAY TO DO THE ENUM YET?? + RestorableBlock rblock = (RestorableBlock) restorable; + + outputStream.writeShort(rblock.getId()); + outputStream.writeUTF(rblock.getWorld()); + outputStream.writeInt(rblock.getX()); + outputStream.writeShort(rblock.getY()); + outputStream.writeInt(rblock.getZ()); + outputStream.write((byte) rblock.getData()); + outputStream.writeShort(rblock.getItems().size()); + + // Write the items if there are any + for (Map.Entry entry : rblock.getItems().entrySet()) { + int slot = entry.getKey(); + ItemStack stack = entry.getValue(); + + outputStream.writeShort(slot); + outputStream.writeShort(stack.getTypeId()); + outputStream.writeShort(stack.getAmount()); + outputStream.writeShort(stack.getDurability()); + } + } + + outputStream.flush(); + } + + /** + * Read the backup's header + * + * @throws IOException + */ + protected void readHeader() throws IOException { + revision = inputStream.readShort(); + created = inputStream.readLong(); + inputStream.read(new byte[10]); // reserved space + } + + /** + * Write the backup's header + * + * @throws IOException + */ + protected void writeHeader() throws IOException { + outputStream.writeShort(revision); + outputStream.writeLong(created); + outputStream.write(new byte[10]); // reserved space + outputStream.flush(); + } + + /** + * Close the backup file + * + * @throws IOException + */ + protected void close() throws IOException { + if (operationMode == OperationMode.READ) { + inputStream.close(); + } else if (operationMode == OperationMode.WRITE) { + outputStream.close(); + } + } + +} diff --git a/core/src/main/java/com/griefcraft/io/BackupManager.java b/core/src/main/java/com/griefcraft/io/BackupManager.java new file mode 100644 index 000000000..587ac03a2 --- /dev/null +++ b/core/src/main/java/com/griefcraft/io/BackupManager.java @@ -0,0 +1,367 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.io; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.model.Protection; +import com.griefcraft.sql.Database; +import com.griefcraft.sql.PhysDB; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.block.Block; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; + +import java.io.File; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.Statement; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; + +public class BackupManager { + + /** + * The result for a backup operationMode + */ + public enum Result { + OK, FAILURE + } + + /** + * The folder where backups are stored at + */ + public static String BACKUP_FOLDER = "plugins/LWC/backups/"; + + /** + * The date format to name backup files with by default + */ + private static String DATE_FORMAT = "MM-dd-yyyy-HHmm"; + + /** + * The file extension for compressed backups + */ + private static String FILE_EXTENSION_COMPRESSED = ".lwc.gz"; + + /** + * The file extension of uncompressed backups + */ + private static String FILE_EXTENSION_UNCOMPRESSED = ".lwc"; + + /** + * The amount of protection block gets to batch at once + */ + private static int BATCH_SIZE = 250; + + /** + * The folder backups are stored in + */ + private final File backupFolder = new File(BACKUP_FOLDER); + + /** + * Backup creation flags + */ + public enum Flag { + + /** + * Backup protection objects + */ + BACKUP_PROTECTIONS, + + /** + * Backup blocks along with their inventory contents (if applicable) also + */ + BACKUP_BLOCKS, + + /** + * Compress the backup using GZip + */ + COMPRESSION + + } + + public BackupManager() { + if (!backupFolder.exists()) { + backupFolder.mkdir(); + } + } + + /** + * Begin restoring a backup. This should be ran in a separate thread. + * Any world calls are offloaded to the world thread using the scheduler. No world reads are done, only writes. + * + * @param name + * @return OK if successful, otherwise FAILURE + */ + public Result restoreBackup(String name) { + try { + Backup backup = loadBackup(name); + + if (backup == null) { + return Result.FAILURE; + } + + return restoreBackup(backup); + } catch (IOException e) { + System.out.println("[BackupManager] Caught: " + e.getMessage()); + return Result.FAILURE; + } + } + + /** + * Begin restoring a backup. This should be ran in a separate thread. + * Any world calls are offloaded to the world thread using the scheduler. No world reads are done, only writes. + * + * @param backup + * @return OK if successful, otherwise FAILURE + */ + public Result restoreBackup(Backup backup) { + try { + // Read in the backup's header + backup.readHeader(); + + // begin restoring :) + Restorable restorable; + int count = 0; + int protectionCount = 0; + int blockCount = 0; + while ((restorable = backup.readRestorable()) != null) { + restorable.restore(); + + if (count % 2000 == 0) { + System.out.println("[Backup] Restored restorables: " + count); + } + count ++; + + // TODO THIS IS HACKS :-( ALSO ENUM ENUM + if (restorable.getType() == 0) { + protectionCount ++; + } else if (restorable.getType() == 1) { + blockCount ++; + } + } + + System.out.println(String.format("[BackupManager] Restored %d restorables. %d were protections, %d blocks.", count, protectionCount, blockCount)); + return Result.OK; + } catch (IOException e) { + e.printStackTrace(); + return Result.FAILURE; + } + } + + /** + * Load a backup + * + * @param name + * @return + */ + public Backup loadBackup(String name) throws IOException { + File file; + + // Try to load the compressed version + file = new File(BACKUP_FOLDER, name + FILE_EXTENSION_COMPRESSED); + + if (file.exists()) { + // Bingo + return new Backup(file, Backup.OperationMode.READ, EnumSet.of(Flag.COMPRESSION)); + } + + // Try uncompressed + file = new File(BACKUP_FOLDER, name + FILE_EXTENSION_UNCOMPRESSED); + + if (file.exists()) { + return new Backup(file, Backup.OperationMode.READ, EnumSet.noneOf(Flag.class)); + } + + // Nothing :-( + return null; + } + + /** + * Create a backup of the given objects. + * When this returns, it is not guaranteed that the backup is fully written to the disk. + * + * @param name + * @param flags + * @return + */ + public Backup createBackup(String name, final EnumSet flags) { + final LWC lwc = LWC.getInstance(); + final Plugin plugin = lwc.getPlugin(); + Server server = Bukkit.getServer(); + final BukkitScheduler scheduler = server.getScheduler(); + String extension = flags.contains(Flag.COMPRESSION) ? FILE_EXTENSION_COMPRESSED : FILE_EXTENSION_UNCOMPRESSED; + File backupFile = new File(backupFolder, name + extension); + + // Our backup file + try { + final Backup backup = new Backup(backupFile, Backup.OperationMode.WRITE, flags); + + scheduler.scheduleAsyncDelayedTask(plugin, new Runnable() { + public void run() { + try { + System.out.println("Processing backup request now in a separate thread"); + + // the list of protections work off of. We batch updates to the world + // so we can more than 20 results/second. + final List protections = new ArrayList(BATCH_SIZE); + + // amount of protections + int totalProtections = lwc.getPhysicalDatabase().getProtectionCount(); + + // Write the header + backup.writeHeader(); + + // TODO separate stream logic to somewhere else :) + // Create a new database connection, we are just reading + PhysDB database = new PhysDB(); + database.connect(); + database.load(); + + // TODO separate stream logic to somewhere else :) + Statement resultStatement = database.getConnection().createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + + if (lwc.getPhysicalDatabase().getType() == Database.Type.MySQL) { + resultStatement.setFetchSize(Integer.MIN_VALUE); + } + + String prefix = lwc.getPhysicalDatabase().getPrefix(); + ResultSet result = resultStatement.executeQuery("SELECT id, owner, type, x, y, z, data, blockId, world, password, date, last_accessed FROM " + prefix + "protections"); + int count = 0; + + while (result.next()) { + final Protection tprotection = database.resolveProtection(result); + + if (count % 2000 == 0) { + System.out.println("[Backup] Parsed protections: " + count + "/" + totalProtections); + } + count ++; + + if (protections.size() != BATCH_SIZE) { + // Wait until we have BATCH_SIZE protections + protections.add(tprotection); + + if (protections.size() != totalProtections) { + continue; + } + } + + // Get all of the blocks in the world + Future getBlocks = scheduler.callSyncMethod(plugin, new Callable() { + public Void call() throws Exception { + for (Protection protection : protections) { + protection.getBlock(); // this will cache it also :D + } + + return null; + } + }); + + // Get all of the blocks + getBlocks.get(); + + for (Protection protection : protections) { + try { + // if we are writing the block to the backup, do that before we write the protection + if (flags.contains(Flag.BACKUP_BLOCKS)) { + // now we can get the block from the world + Block block = protection.getBlock(); + + // Wrap the block object in a RestorableBlock object + RestorableBlock rblock = RestorableBlock.wrapBlock(block); + + // Write it + backup.writeRestorable(rblock); + } + + // Now write the protection after the block if we are writing protections + if (flags.contains(Flag.BACKUP_PROTECTIONS)) { + RestorableProtection rprotection = RestorableProtection.wrapProtection(protection); + + // Write it + backup.writeRestorable(rprotection); + } + } catch (Exception e) { + System.out.println("Caught: " + e.getMessage() + ". Carrying on..."); + } + } + + // Clear the protection set, we are done with them + protections.clear(); + } + + // close the sql statements + result.close(); + resultStatement.close(); + + // close the backup file + backup.close(); + + System.out.println("Backup completed!"); + } catch (Exception e) { // database.connect() throws Exception + System.out.println("Backup exception caught: " + e.getMessage()); + } + } + }); + + return backup; + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + /** + * Create a backup of all protections, blocks, and their contents + * When this returns, it is not guaranteed that the backup is fully written to the disk. + * + * @param flags + * @return + */ + public Backup createBackup(EnumSet flags) { + return createBackup(new SimpleDateFormat(DATE_FORMAT).format(new Date()), flags); + } + + /** + * Create a backup of all protections, blocks, and their contents + * When this returns, it is not guaranteed that the backup is fully written to the disk. + * + * @return + */ + public Backup createBackup() { + return createBackup(EnumSet.of(Flag.COMPRESSION, Flag.BACKUP_BLOCKS, Flag.BACKUP_PROTECTIONS)); + } + +} diff --git a/core/src/main/java/com/griefcraft/io/Restorable.java b/core/src/main/java/com/griefcraft/io/Restorable.java new file mode 100644 index 000000000..069aac9f6 --- /dev/null +++ b/core/src/main/java/com/griefcraft/io/Restorable.java @@ -0,0 +1,46 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.io; + +public interface Restorable { + + /** + * Get the restorable type + * + * TODO Enum + * @return + */ + public int getType(); + + /** + * Restore the object into the world + */ + public void restore(); + +} diff --git a/core/src/main/java/com/griefcraft/io/RestorableBlock.java b/core/src/main/java/com/griefcraft/io/RestorableBlock.java new file mode 100644 index 000000000..275e4740d --- /dev/null +++ b/core/src/main/java/com/griefcraft/io/RestorableBlock.java @@ -0,0 +1,233 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.io; + +import com.griefcraft.lwc.LWC; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Map; + +public class RestorableBlock implements Restorable { + + /** + * The block id + */ + private int id; + + /** + * The world's name + */ + private String world; + + /** + * The x coordinate + */ + private int x; + + /** + * The y coordinate + */ + private int y; + + /** + * The z coordinate + */ + private int z; + + /** + * The block data + */ + private int data; + + /** + * The items in this block's inventory if it has one + */ + private final Map items = new HashMap(); + + public int getType() { + return 1; // TODO ENUM, HOPEFULLY I'LL REMEMBER IF I PUT THIS TODO EVERYWHERE + } + + public void restore() { + LWC lwc = LWC.getInstance(); + + lwc.getPlugin().getServer().getScheduler().scheduleSyncDelayedTask(lwc.getPlugin(), new Runnable() { + public void run() { + Server server = Bukkit.getServer(); + + // Get the world + World bworld = server.getWorld(world); + + // Not found :-( + if (world == null) { + return; + } + + // Get the block we want + Block block = bworld.getBlockAt(x, y, z); + + // Begin screwing with shit :p + block.setTypeId(id); + block.setData((byte) data); + + if (items.size() > 0) { + if (!(block.getState() instanceof InventoryHolder)) { + System.out.println(String.format("The block at [%d, %d, %d] has backed up items but no longer supports them. Why? %s", x, y, z, block.toString())); + } + + // Get the block's inventory + Inventory inventory = ((InventoryHolder) block.getState()).getInventory(); + + // Set all of the items to it + for (Map.Entry entry : items.entrySet()) { + int slot = entry.getKey(); + ItemStack stack = entry.getValue(); + + if (stack == null) { + continue; + } + + // Add it to the inventory + inventory.setItem(slot, stack); + } + } + } + }); + } + + /** + * Wrap a block in a restorableblock object + * + * @param block + * @return + */ + public static RestorableBlock wrapBlock(Block block) { + if (block == null) { + return null; + } + + RestorableBlock rblock = new RestorableBlock(); + rblock.id = block.getTypeId(); + rblock.world = block.getWorld().getName(); + rblock.x = block.getX(); + rblock.y = block.getY(); + rblock.z = block.getZ(); + rblock.data = block.getData(); + + BlockState state = block.getState(); + + // Does it have an inventory? ^^ + if (state instanceof InventoryHolder) { + Inventory inventory = ((InventoryHolder) state).getInventory(); + ItemStack[] stacks = inventory.getContents(); + + for (int slot = 0; slot < stacks.length; slot++) { + ItemStack stack = stacks[slot]; + + if (stack == null) { + continue; // don't waste space! + } + + rblock.setSlot(slot, stack); + } + } + + return rblock; + } + + /** + * Set a slot in the inventory + * + * @param slot + * @param stack + */ + public void setSlot(int slot, ItemStack stack) { + items.put(slot, stack); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getWorld() { + return world; + } + + public void setWorld(String world) { + this.world = world; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getZ() { + return z; + } + + public void setZ(int z) { + this.z = z; + } + + public int getData() { + return data; + } + + public void setData(int data) { + this.data = data; + } + + public Map getItems() { + return items; + } +} diff --git a/core/src/main/java/com/griefcraft/io/RestorableProtection.java b/core/src/main/java/com/griefcraft/io/RestorableProtection.java new file mode 100644 index 000000000..094a02335 --- /dev/null +++ b/core/src/main/java/com/griefcraft/io/RestorableProtection.java @@ -0,0 +1,224 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.io; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.model.Protection; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +public class RestorableProtection implements Restorable { + + /** + * The id in the database + */ + private int id; + + /** + * The protection type + */ + private int protectionType; + + /** + * The block id + */ + private int blockId; + + /** + * The protection owner + */ + private String owner; + + /** + * The world the protection is int + */ + private String world; + + /** + * The x coordinate + */ + private int x; + + /** + * The y coordinate + */ + private int y; + + /** + * The z coordinate + */ + private int z; + + /** + * Data for the protection (acls, flags) + */ + private String data; + + /** + * Epoch when it was created + */ + private long created; + + /** + * Epoch when it was last updated + */ + private long updated; + + public int getType() { + return 0; // TODO ENUM ENUM ENUM ENUM ENUM ENUM + } + + public void restore() { + LWC lwc = LWC.getInstance(); + Protection protection = lwc.getPhysicalDatabase().registerProtection(blockId, Protection.Type.values()[protectionType], + world, owner, data, x, y, z); + // TODO fix the ID? + } + + /** + * Wrap a protection object around a RestorableProtection object + * + * @param protection + * @return + */ + public static RestorableProtection wrapProtection(Protection protection) { + if (protection == null) { + return null; + } + + try { + RestorableProtection rprotection = new RestorableProtection(); + rprotection.id = protection.getId(); + rprotection.protectionType = protection.getType().ordinal(); + rprotection.blockId = protection.getBlockId(); + rprotection.owner = protection.getOwner(); + rprotection.world = protection.getWorld(); + rprotection.x = protection.getX(); + rprotection.y = protection.getY(); + rprotection.z = protection.getZ(); + rprotection.data = protection.getData().toJSONString(); + rprotection.created = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(protection.getCreation()).getTime() / 1000; + rprotection.updated = protection.getLastAccessed(); + + return rprotection; + } catch (ParseException e) { + System.out.println("Failed to wrap protection: " + protection + " " + e.getMessage()); + return null; + } + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getProtectionType() { + return protectionType; + } + + public void setProtectionType(int protectionType) { + this.protectionType = protectionType; + } + + public int getBlockId() { + return blockId; + } + + public void setBlockId(int blockId) { + this.blockId = blockId; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getWorld() { + return world; + } + + public void setWorld(String world) { + this.world = world; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getZ() { + return z; + } + + public void setZ(int z) { + this.z = z; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public long getCreated() { + return created; + } + + public void setCreated(long created) { + this.created = created; + } + + public long getUpdated() { + return updated; + } + + public void setUpdated(long updated) { + this.updated = updated; + } +} diff --git a/core/src/main/java/com/griefcraft/listeners/LWCBlockListener.java b/core/src/main/java/com/griefcraft/listeners/LWCBlockListener.java new file mode 100644 index 000000000..65fd1acac --- /dev/null +++ b/core/src/main/java/com/griefcraft/listeners/LWCBlockListener.java @@ -0,0 +1,516 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.listeners; + +import com.griefcraft.cache.ProtectionCache; +import com.griefcraft.lwc.LWC; +import com.griefcraft.lwc.LWCPlugin; +import com.griefcraft.model.Protection; +import com.griefcraft.scripting.event.LWCProtectionDestroyEvent; +import com.griefcraft.scripting.event.LWCProtectionRegisterEvent; +import com.griefcraft.scripting.event.LWCProtectionRegistrationPostEvent; +import com.griefcraft.scripting.event.LWCRedstoneEvent; +import com.griefcraft.util.Colors; +import com.griefcraft.util.matchers.DoubleChestMatcher; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockMultiPlaceEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.BlockRedstoneEvent; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.world.StructureGrowEvent; +import org.bukkit.material.MaterialData; +import org.bukkit.material.PistonBaseMaterial; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LWCBlockListener implements Listener { + + /** + * The plugin instance + */ + private LWCPlugin plugin; + + /** + * A set of blacklisted blocks + */ + private final Set blacklistedBlocks = new HashSet(); + + public LWCBlockListener(LWCPlugin plugin) { + this.plugin = plugin; + loadAndProcessConfig(); + } + + @EventHandler + public void onBlockRedstoneChange(BlockRedstoneEvent event) { + if (!LWC.ENABLED) { + return; + } + + LWC lwc = plugin.getLWC(); + Block block = event.getBlock(); + + if (block == null) { + return; + } + + Protection protection = lwc.findProtection(block.getLocation()); + + if (protection == null) { + return; + } + + LWCRedstoneEvent evt = new LWCRedstoneEvent(event, protection); + lwc.getModuleLoader().dispatchEvent(evt); + + if (evt.isCancelled()) { + event.setNewCurrent(event.getOldCurrent()); + } + } + + @EventHandler + public void onStructureGrow(StructureGrowEvent event) { + if (!LWC.ENABLED) { + return; + } + + LWC lwc = LWC.getInstance(); + // the blocks that were changed / replaced + List blocks = event.getBlocks(); + + for (BlockState block : blocks) { + if (!lwc.isProtectable(block.getBlock())) { + continue; + } + + // we don't have the block id of the block before it + // so we have to do some raw lookups (these are usually cache hits however, at least!) + Protection protection = lwc.getPhysicalDatabase().loadProtection(block.getWorld().getName(), block.getX(), block.getY(), block.getZ()); + + if (protection != null) { + event.setCancelled(true); + } + } + } + + @EventHandler + public void onSignChange(SignChangeEvent event) { + if (!LWC.ENABLED || event.isCancelled()) { + return; + } + + LWC lwc = plugin.getLWC(); + Block block = event.getBlock(); + Player player = event.getPlayer(); + + if (block == null) { + return; + } + + Protection protection = lwc.findProtection(block.getLocation()); + + if (protection == null) { + return; + } + + boolean canAccess = lwc.canAccessProtection(player, protection); + + if (!canAccess) { + event.setCancelled(true); + } + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + if (!LWC.ENABLED || event.isCancelled()) { + return; + } + + LWC lwc = plugin.getLWC(); + Player player = event.getPlayer(); + Block block = event.getBlock(); + + boolean ignoreBlockDestruction = Boolean.parseBoolean(lwc.resolveProtectionConfiguration(block, "ignoreBlockDestruction")); + + if (ignoreBlockDestruction) { + return; + } + + ProtectionCache cache = lwc.getProtectionCache(); + String cacheKey = cache.cacheKey(block.getLocation()); + + // In the event they place a block, remove any known nulls there + if (cache.isKnownNull(cacheKey)) { + cache.remove(cacheKey); + } + + Protection protection = lwc.findProtection(block.getLocation()); + + if (protection == null) { + return; + } + + boolean canAccess = lwc.canAccessProtection(player, protection); + boolean canAdmin = lwc.canAdminProtection(player, protection); + + // when destroying a chest, it's possible they are also destroying a double chest + // in the event they're trying to destroy a double chest, we should just move + // the protection to the chest that is not destroyed, if it is not that one already. + if (protection.isOwner(player) && DoubleChestMatcher.PROTECTABLES_CHESTS.contains(block.getType())) { + Block doubleChest = lwc.findAdjacentDoubleChest(block); + + if (doubleChest != null) { + // if they destroyed the protected block we want to move it aye? + if (lwc.blockEquals(protection.getBlock(), block)) { + // correct the block + protection.setBlockId(doubleChest.getTypeId()); + protection.setX(doubleChest.getX()); + protection.setY(doubleChest.getY()); + protection.setZ(doubleChest.getZ()); + protection.saveNow(); + } + + // Repair the cache + protection.radiusRemoveCache(); + + if (protection.getProtectionFinder() != null) { + protection.getProtectionFinder().removeBlock(block.getState()); + } + + lwc.getProtectionCache().addProtection(protection); + + return; + } + } + + try { + LWCProtectionDestroyEvent evt = new LWCProtectionDestroyEvent(player, protection, LWCProtectionDestroyEvent.Method.BLOCK_DESTRUCTION, canAccess, canAdmin); + lwc.getModuleLoader().dispatchEvent(evt); + + if (evt.isCancelled() || !canAccess) { + event.setCancelled(true); + } + } catch (Exception e) { + event.setCancelled(true); + lwc.sendLocale(player, "protection.internalerror", "id", "BLOCK_BREAK"); + e.printStackTrace(); + } + } + + @EventHandler + public void onBlockPistonRetract(BlockPistonRetractEvent event) { + if (!LWC.ENABLED || event.isCancelled()) { + return; + } + + LWC lwc = plugin.getLWC(); + Block piston = event.getBlock(); + BlockState state = piston.getState(); + MaterialData data = state.getData(); + BlockFace direction = null; + + // Check the block it pushed directly + if (data instanceof PistonBaseMaterial) { + direction = ((PistonBaseMaterial) data).getFacing(); + } + + if (direction == null) { + return; + } + + // the block that the piston moved + Block moved = piston.getRelative(direction, 2); + + // TODO remove this when spout fixes their shit + if (moved.getType() == Material.WOODEN_DOOR || moved.getType() == Material.IRON_DOOR_BLOCK) { + Block below = moved.getRelative(BlockFace.DOWN).getRelative(direction.getOppositeFace()); + + if (lwc.findProtection(below.getLocation()) != null) { + event.setCancelled(true); + return; + } + } + + if (lwc.findProtection(moved.getLocation()) != null) { + event.setCancelled(true); + } + } + + @EventHandler + public void onBlockPistonExtend(BlockPistonExtendEvent event) { + if (!LWC.ENABLED || event.isCancelled()) { + return; + } + + LWC lwc = plugin.getLWC(); + Block piston = event.getBlock(); + BlockState state = piston.getState(); + MaterialData data = state.getData(); + BlockFace direction = null; + + // Check the block it pushed directly + if (data instanceof PistonBaseMaterial) { + direction = ((PistonBaseMaterial) data).getFacing(); + Block block = event.getBlock().getRelative(direction); + + Protection protection = lwc.findProtection(block.getLocation()); + + if (protection != null) { + event.setCancelled(true); + return; + } + } + + // if no direction was found, no point in going on + if (direction == null) { + return; + } + + // Check the affected blocks + for (int i = 0; i < event.getLength() + 2; i++) { + Block block = piston.getRelative(direction, i); + Protection protection = lwc.findProtection(block.getLocation()); + + // We don't want that! + if (block.getType() == Material.AIR) { + break; + } + + if (protection != null) { + event.setCancelled(true); + break; + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBlockPlace(BlockPlaceEvent event) { + if (!LWC.ENABLED) { + return; + } + + LWC lwc = plugin.getLWC(); + Player player = event.getPlayer(); + Block block = event.getBlockPlaced(); + + ProtectionCache cache = lwc.getProtectionCache(); + String cacheKey = cache.cacheKey(block.getLocation()); + + // In the event they place a block, remove any known nulls there + if (cache.isKnownNull(cacheKey)) { + cache.remove(cacheKey); + } + + // check if the block is blacklisted + boolean blockIsBlacklisted = blacklistedBlocks.contains(block.getTypeId()) || blacklistedBlocks.contains(hashCode(block.getTypeId(), block.getData())); + + if (blockIsBlacklisted) { + // it's blacklisted, check for a protected chest + for (Protection protection : lwc.findAdjacentProtectionsOnAllSides(block)) { + if (protection != null) { + if (!lwc.canAccessProtection(player, protection) || (protection.getType() == Protection.Type.DONATION && !lwc.canAdminProtection(player, protection))) { + // they can't access the protection .. + event.setCancelled(true); + return; + } + } + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBlockMultiPlace(BlockMultiPlaceEvent event) { + LWC lwc = plugin.getLWC(); + Block block = event.getBlock(); + + if (block.getType() == Material.BED_BLOCK) { + for (BlockState state : event.getReplacedBlockStates()) { + Protection protection = lwc.findProtection(state); + + if (protection != null) { + event.setCancelled(true); + return; + } + } + } + } + + /** + * Used for auto registering placed protections + */ + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onBlockPlaceMonitor(BlockPlaceEvent event) { + if (!LWC.ENABLED) { + return; + } + + LWC lwc = plugin.getLWC(); + Player player = event.getPlayer(); + Block block = event.getBlockPlaced(); + + // Update the cache if a protection is matched here + Protection current = lwc.findProtection(block.getLocation()); + if (current != null) { + if (!current.isBlockInWorld()) { + // Corrupted protection + lwc.log("Removing corrupted protection: " + current); + current.remove(); + } else { + if (current.getProtectionFinder() != null) { + current.getProtectionFinder().fullMatchBlocks(); + lwc.getProtectionCache().addProtection(current); + } + + return; + } + } + + // The placable block must be protectable + if (!lwc.isProtectable(block)) { + return; + } + + String autoRegisterType = lwc.resolveProtectionConfiguration(block, "autoRegister"); + + // is it auto protectable? + if (!autoRegisterType.equalsIgnoreCase("private") && !autoRegisterType.equalsIgnoreCase("public")) { + return; + } + + if (!lwc.hasPermission(player, "lwc.create." + autoRegisterType, "lwc.create", "lwc.protect")) { + return; + } + + // Parse the type + Protection.Type type; + + try { + type = Protection.Type.valueOf(autoRegisterType.toUpperCase()); + } catch (IllegalArgumentException e) { + // No auto protect type found + return; + } + + // Is it okay? + if (type == null) { + player.sendMessage(Colors.Red + "LWC_INVALID_CONFIG_autoRegister"); + return; + } + + // If it's a chest, make sure they aren't placing it beside an already registered chest + if (DoubleChestMatcher.PROTECTABLES_CHESTS.contains(block.getType())) { + BlockFace[] faces = new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}; + + for (BlockFace blockFace : faces) { + Block face = block.getRelative(blockFace); + + //They're placing it beside a chest, check if it's already protected + if (face.getType() == block.getType()) { + if (lwc.findProtection(face.getLocation()) != null) { + return; + } + } + } + } + + try { + LWCProtectionRegisterEvent evt = new LWCProtectionRegisterEvent(player, block); + lwc.getModuleLoader().dispatchEvent(evt); + + // something cancelled registration + if (evt.isCancelled()) { + return; + } + + // All good! + Protection protection = lwc.getPhysicalDatabase().registerProtection(block.getTypeId(), type, block.getWorld().getName(), player.getUniqueId().toString(), "", block.getX(), block.getY(), block.getZ()); + + if (!Boolean.parseBoolean(lwc.resolveProtectionConfiguration(block, "quiet"))) { + lwc.sendLocale(player, "protection.onplace.create.finalize", "type", lwc.getPlugin().getMessageParser().parseMessage(autoRegisterType.toLowerCase()), "block", LWC.materialToString(block)); + } + + if (protection != null) { + lwc.getModuleLoader().dispatchEvent(new LWCProtectionRegistrationPostEvent(protection)); + } + } catch (Exception e) { + lwc.sendLocale(player, "protection.internalerror", "id", "PLAYER_INTERACT"); + e.printStackTrace(); + } + } + + /** + * Load and process the configuration + */ + public void loadAndProcessConfig() { + List ids = LWC.getInstance().getConfiguration().getStringList("optional.blacklistedBlocks", new ArrayList()); + + for (String sId : ids) { + String[] idParts = sId.trim().split(":"); + + int id = Integer.parseInt(idParts[0].trim()); + int data = 0; + + if (idParts.length > 1) { + data = Integer.parseInt(idParts[1].trim()); + } + + if (data == 0) { + blacklistedBlocks.add(id); + } else { + blacklistedBlocks.add(hashCode(id, data)); + } + } + } + + /** + * Get the hashcode of two integers + * + * @param int1 + * @param int2 + * @return + */ + private int hashCode(int int1, int int2) { + int hash = int1 * 17; + hash *= 37 + int2; + return hash; + } + +} diff --git a/core/src/main/java/com/griefcraft/listeners/LWCEntityListener.java b/core/src/main/java/com/griefcraft/listeners/LWCEntityListener.java new file mode 100644 index 000000000..4a8d0322d --- /dev/null +++ b/core/src/main/java/com/griefcraft/listeners/LWCEntityListener.java @@ -0,0 +1,168 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.listeners; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.lwc.LWCPlugin; +import com.griefcraft.model.Flag; +import com.griefcraft.model.Protection; +import com.nitnelave.CreeperHeal.config.CreeperConfig; +import com.nitnelave.CreeperHeal.config.WorldConfig; +import org.bukkit.block.Block; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityBreakDoorEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityInteractEvent; +import org.bukkit.plugin.Plugin; + +public class LWCEntityListener implements Listener { + + /** + * The plugin instance + */ + private LWCPlugin plugin; + + public LWCEntityListener(LWCPlugin plugin) { + this.plugin = plugin; + } + + @EventHandler + public void entityInteract(EntityInteractEvent event) { + Block block = event.getBlock(); + + Protection protection = plugin.getLWC().findProtection(block.getLocation()); + + if (protection != null) { + boolean allowEntityInteract = Boolean.parseBoolean(plugin.getLWC().resolveProtectionConfiguration(block, "allowEntityInteract")); + + if (!allowEntityInteract) { + event.setCancelled(true); + } + } + } + + @EventHandler + public void entityBreakDoor(EntityBreakDoorEvent event) { + Block block = event.getBlock(); + + // See if there is a protection there + Protection protection = plugin.getLWC().findProtection(block.getLocation()); + + if (protection != null) { + // protections.allowEntityBreakDoor + boolean allowEntityBreakDoor = Boolean.parseBoolean(plugin.getLWC().resolveProtectionConfiguration(block, "allowEntityBreakDoor")); + + if (!allowEntityBreakDoor) { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onEntityExplode(EntityExplodeEvent event) { + if (!LWC.ENABLED || event.isCancelled()) { + return; + } + + LWC lwc = LWC.getInstance(); + + for (Block block : event.blockList()) { + Protection protection = plugin.getLWC().findProtection(block.getLocation()); + + if (protection != null) { + boolean ignoreExplosions = Boolean.parseBoolean(lwc.resolveProtectionConfiguration(protection.getBlock(), "ignoreExplosions")); + + if (!(ignoreExplosions || protection.hasFlag(Flag.Type.ALLOWEXPLOSIONS))) { + event.setCancelled(true); + } + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onEntityExplodeMonitor(EntityExplodeEvent event) { + if (!LWC.ENABLED || event.isCancelled()) { + return; + } + + LWC lwc = LWC.getInstance(); + + for (Block block : event.blockList()) { + Protection protection = plugin.getLWC().findProtection(block.getLocation()); + + if (protection != null) { + boolean ignoreExplosions = Boolean.parseBoolean(lwc.resolveProtectionConfiguration(protection.getBlock(), "ignoreExplosions")); + + if (ignoreExplosions || protection.hasFlag(Flag.Type.ALLOWEXPLOSIONS)) { + // If creeper heal is active for the block, halt all thrusters! + if (isCreeperHealActive(event.getEntity())) { + break; + } + + protection.remove(); + } + } + } + } + + /** + * Check if the CreeperHeal plugin is active. If it is, we shouldn't remove protections + * + * @return + */ + private boolean isCreeperHealActive(Entity entity) { + if (entity == null) { + return false; + } + + Plugin creeperHealPlugin = plugin.getServer().getPluginManager().getPlugin("CreeperHeal"); + + if (creeperHealPlugin != null) { + WorldConfig worldConfig = CreeperConfig.loadWorld(entity.getWorld()); + + if (worldConfig == null) { + return false; // Uh-oh? + } + + if (entity instanceof Creeper) { + return worldConfig.creepers; + } else if (entity instanceof TNTPrimed) { + return worldConfig.tnt; + } + } + + return false; + } + +} diff --git a/core/src/main/java/com/griefcraft/listeners/LWCMCPCSupport.java b/core/src/main/java/com/griefcraft/listeners/LWCMCPCSupport.java new file mode 100644 index 000000000..54d801692 --- /dev/null +++ b/core/src/main/java/com/griefcraft/listeners/LWCMCPCSupport.java @@ -0,0 +1,67 @@ +package com.griefcraft.listeners; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.model.LWCPlayer; +import com.griefcraft.scripting.JavaModule; +import com.griefcraft.scripting.event.LWCProtectionDestroyEvent; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class LWCMCPCSupport extends JavaModule { + + /** + * The shared multiplayer block id used by transport pipes + */ + public static final int TRANSPORT_PIPE_ID = 166; + + /** + * The LWC object + */ + private LWC lwc; + + /** + * A set of blacklisted players that are blocked from destroying any blocks protected by LWC. Mainly useful for MCPC + * where mods can remove blocks and try to break the block by sending an event first (e.g turtle) + */ + private final Set blacklistedPlayers = new HashSet(); + + public LWCMCPCSupport(LWC lwc) { + this.lwc = lwc; + loadAndProcessConfig(); + } + + /** + * Load and process the configuration + */ + public void loadAndProcessConfig() { + blacklistedPlayers.clear(); + + for (String player : lwc.getConfiguration().getStringList("optional.blacklistedPlayers", new ArrayList())) { + blacklistedPlayers.add(player.toLowerCase()); + } + } + + /** + * Called when a protection is destroyed + * + * @param event + */ + public void onDestroyProtection(LWCProtectionDestroyEvent event) { + if (event.isCancelled()) { + return; + } + + Player bPlayer = event.getPlayer(); + LWCPlayer player = lwc.wrapPlayer(bPlayer); + String lowerPlayerName = player.getName().toLowerCase(); + + if (blacklistedPlayers.contains(lowerPlayerName)) { + event.setCancelled(true); + player.sendLocale("protection.accessdenied"); + } + } + +} diff --git a/core/src/main/java/com/griefcraft/listeners/LWCPlayerListener.java b/core/src/main/java/com/griefcraft/listeners/LWCPlayerListener.java new file mode 100644 index 000000000..d2317d5bf --- /dev/null +++ b/core/src/main/java/com/griefcraft/listeners/LWCPlayerListener.java @@ -0,0 +1,517 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.listeners; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.lwc.LWCPlugin; +import com.griefcraft.model.Flag; +import com.griefcraft.model.LWCPlayer; +import com.griefcraft.model.Protection; +import com.griefcraft.scripting.Module; +import com.griefcraft.scripting.event.LWCBlockInteractEvent; +import com.griefcraft.scripting.event.LWCDropItemEvent; +import com.griefcraft.scripting.event.LWCProtectionInteractEvent; +import com.griefcraft.util.UUIDRegistry; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.DoubleChest; +import org.bukkit.block.Hopper; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.Set; + +public class LWCPlayerListener implements Listener { + + /** + * The plugin instance + */ + private LWCPlugin plugin; + + public LWCPlayerListener(LWCPlugin plugin) { + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + UUIDRegistry.updateCache(player.getUniqueId(), player.getName()); + } + + @EventHandler(ignoreCancelled = true) + public void onMoveItem(InventoryMoveItemEvent event) { + boolean result; + + // if the initiator is the same as the source it is a dropper i.e. depositing items + if (event.getInitiator() == event.getSource()) { + result = handleMoveItemEvent(event.getInitiator(), event.getDestination()); + } else { + result = handleMoveItemEvent(event.getInitiator(), event.getSource()); + } + + if (result) { + event.setCancelled(true); + } + } + + /** + * Handle the item move event + * + * @param inventory + */ + private boolean handleMoveItemEvent(Inventory initiator, Inventory inventory) { + LWC lwc = LWC.getInstance(); + + if (inventory == null) { + return false; + } + + Location location; + InventoryHolder holder; + Location hopperLocation = null; + InventoryHolder hopperHolder; + + try { + holder = inventory.getHolder(); + hopperHolder = initiator.getHolder(); + } catch (AbstractMethodError e) { + return false; + } + + try { + if (holder instanceof BlockState) { + location = ((BlockState) holder).getLocation(); + } else if (holder instanceof DoubleChest) { + location = ((DoubleChest) holder).getLocation(); + } else { + return false; + } + + if (hopperHolder instanceof Hopper) { + hopperLocation = ((Hopper) hopperHolder).getLocation(); + } + } catch (Exception e) { + return false; + } + + // High-intensity zone: increase protection cache if it's full, otherwise + // the database will be getting rammed + lwc.getProtectionCache().increaseIfNecessary(); + + // Attempt to load the protection at that location + Protection protection = lwc.findProtection(location); + + // If no protection was found we can safely ignore it + if (protection == null) { + return false; + } + + if (hopperLocation != null && Boolean.parseBoolean(lwc.resolveProtectionConfiguration(Material.HOPPER, "enabled"))) { + Protection hopperProtection = lwc.findProtection(hopperLocation); + + if (hopperProtection != null) { + // if they're owned by the same person then we can allow the move + if (protection.getOwner().equals(hopperProtection.getOwner())) { + return false; + } + } + } + + boolean denyHoppers = Boolean.parseBoolean(lwc.resolveProtectionConfiguration(Material.getMaterial(protection.getBlockId()), "denyHoppers")); + + // xor = (a && !b) || (!a && b) + if (denyHoppers ^ protection.hasFlag(Flag.Type.HOPPER)) { + return true; + } + + return false; + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerDropItem(PlayerDropItemEvent event) { + if (event.isCancelled() || !LWC.ENABLED) { + return; + } + + Player player = event.getPlayer(); + + LWCDropItemEvent evt = new LWCDropItemEvent(player, event); + plugin.getLWC().getModuleLoader().dispatchEvent(evt); + + if (evt.isCancelled()) { + event.setCancelled(true); + } + } + +/* + @EventHandler + public void onPlayerChat(PlayerChatEvent event) { + if (event.isCancelled() || !LWC.ENABLED) { + return; + } + + LWC lwc = plugin.getLWC(); + if (!lwc.getConfiguration().getBoolean("core.filterunlock", true)) { + return; + } + + // We want to block messages starting with cunlock incase someone screws up /cunlock password. + String message = event.getMessage(); + + if (message.startsWith("cunlock") || message.startsWith("lcunlock") || message.startsWith(".cunlock")) { + event.setCancelled(true); + lwc.sendLocale(event.getPlayer(), "lwc.blockedmessage"); + } + } +*/ + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + if (!LWC.ENABLED) { + return; + } + + LWC lwc = plugin.getLWC(); + Player player = event.getPlayer(); + LWCPlayer lwcPlayer = lwc.wrapPlayer(player); + + if (event.getAction() != Action.LEFT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_BLOCK) { + return; + } + + Block block = event.getClickedBlock(); + BlockState state; + + try { + state = block.getState(); + } catch (NullPointerException e) { + // + lwc.log("Invalid Tile Entity detected at " + block.getLocation()); + lwc.log("This is either an issue with your world or a bug in Bukkit"); + return; + } + + // Prevent players with lwc.deny from interacting with blocks that have an inventory + if (state instanceof InventoryHolder && lwc.isProtectable(block)) { + if (!lwc.hasPermission(player, "lwc.protect") && lwc.hasPermission(player, "lwc.deny") && !lwc.isAdmin(player) && !lwc.isMod(player)) { + lwc.sendLocale(player, "protection.interact.error.blocked"); + event.setCancelled(true); + return; + } + } + + try { + Set actions = lwcPlayer.getActionNames(); + Protection protection = lwc.findProtection(block.getLocation()); + Module.Result result; + boolean canAccess = lwc.canAccessProtection(player, protection); + + // Calculate if the player has a pending action (i.e any action besides 'interacted') + int actionCount = actions.size(); + boolean hasInteracted = actions.contains("interacted"); + boolean hasPendingAction = (hasInteracted && actionCount > 1) || (!hasInteracted && actionCount > 0); + + if (event.getAction() == Action.LEFT_CLICK_BLOCK) { + boolean ignoreLeftClick = Boolean.parseBoolean(lwc.resolveProtectionConfiguration(block, "ignoreLeftClick")); + + if (ignoreLeftClick) { + return; + } + } else if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { + boolean ignoreRightClick = Boolean.parseBoolean(lwc.resolveProtectionConfiguration(block, "ignoreRightClick")); + + if (ignoreRightClick) { + return; + } + } + + // If the event was cancelled and they have an action, warn them + if (event.isCancelled()) { + // only send it if a non-"interacted" action is set which is always set on the player + if (hasPendingAction) { + lwc.sendLocale(player, "lwc.pendingaction"); + } + + // it's cancelled, do not continue ! + return; + } + + // register in an action what protection they interacted with (if applicable.) + if (protection != null) { + com.griefcraft.model.Action action = new com.griefcraft.model.Action(); + action.setName("interacted"); + action.setPlayer(lwcPlayer); + action.setProtection(protection); + + lwcPlayer.addAction(action); + } + + // events are only used when they already have an action pending + boolean canAdmin = lwc.canAdminProtection(player, protection); + + if (protection != null) { + LWCProtectionInteractEvent evt = new LWCProtectionInteractEvent(event, protection, actions, canAccess, canAdmin); + lwc.getModuleLoader().dispatchEvent(evt); + + result = evt.getResult(); + } else { + LWCBlockInteractEvent evt = new LWCBlockInteractEvent(event, block, actions); + lwc.getModuleLoader().dispatchEvent(evt); + + result = evt.getResult(); + } + + if (result == Module.Result.ALLOW) { + return; + } + + // optional.onlyProtectIfOwnerIsOnline + if (protection != null && !canAccess && lwc.getConfiguration().getBoolean("optional.onlyProtectWhenOwnerIsOnline", false)) { + Player owner = protection.getBukkitOwner(); + + // If they aren't online, allow them in :P + if (owner == null || !owner.isOnline()) { + return; + } + } + + // optional.onlyProtectIfOwnerIsOffline + if (protection != null && !canAccess && lwc.getConfiguration().getBoolean("optional.onlyProtectWhenOwnerIsOffline", false)) { + Player owner = protection.getBukkitOwner(); + + // If they aren't online, allow them in :P + if (owner != null && owner.isOnline()) { + return; + } + } + + if (result == Module.Result.DEFAULT) { + canAccess = lwc.enforceAccess(player, protection, block, canAccess); + } + + if (!canAccess || result == Module.Result.CANCEL) { + event.setCancelled(true); + event.setUseInteractedBlock(org.bukkit.event.Event.Result.DENY); + } + } catch (Exception e) { + event.setCancelled(true); + event.setUseInteractedBlock(org.bukkit.event.Event.Result.DENY); + lwc.sendLocale(player, "protection.internalerror", "id", "PLAYER_INTERACT"); + e.printStackTrace(); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent event) { + if (!LWC.ENABLED) { + return; + } + + // remove the place from the player cache and reset anything they can access + LWCPlayer.removePlayer(event.getPlayer()); + } + + @EventHandler(ignoreCancelled = true) + public void onInventoryClick(InventoryClickEvent event) { + LWC lwc = LWC.getInstance(); + + if (!(event.getWhoClicked() instanceof Player)) { + return; + } + + // Player interacting with the inventory + Player player = (Player) event.getWhoClicked(); + + // The inventory they are using + Inventory inventory = event.getInventory(); + + if (inventory == null || event.getSlot() < 0) { + return; + } + + // Location of the container + Location location; + InventoryHolder holder = null; + + try { + holder = event.getInventory().getHolder(); + } catch (AbstractMethodError e) { + lwc.log("Caught issue with Bukkit's Inventory.getHolder() method! This is occuring NEAR the player: " + player.getName()); + lwc.log("This player is located at: " + player.getLocation().toString()); + lwc.log("This should be reported to the Bukkit developers."); + e.printStackTrace(); + return; + } + + try { + if (holder instanceof BlockState) { + location = ((BlockState) holder).getLocation(); + } else if (holder instanceof DoubleChest) { + location = ((DoubleChest) holder).getLocation(); + } else { + return; + } + } catch (Exception e) { + Location ploc = player.getLocation(); + String holderName = holder != null ? holder.getClass().getSimpleName() : "Unknown Block"; + lwc.log("Exception with getting the location of a " + holderName + " has occurred NEAR the player: " + player.getName() + " [" + ploc.getBlockX() + " " + ploc.getBlockY() + " " + ploc.getBlockZ() + "]"); + lwc.log("The exact location of the block is not possible to obtain. This is caused by a Minecraft or Bukkit exception normally."); + e.printStackTrace(); + return; + } + + if (event.getAction() != InventoryAction.COLLECT_TO_CURSOR) { + // If it's not a container, we don't want it + if (event.getSlotType() != InventoryType.SlotType.CONTAINER) { + return; + } + + // Nifty trick: these will different IFF they are interacting with the player's inventory or hotbar instead of the block's inventory + if (event.getSlot() != event.getRawSlot()) { + return; + } + + // The item they are taking/swapping with + ItemStack item; + + try { + item = event.getCurrentItem(); + } catch (ArrayIndexOutOfBoundsException e) { + return; + } + + // Item their cursor has + ItemStack cursor = event.getCursor(); + + if (item == null || item.getType() == null || item.getType() == Material.AIR) { + return; + } + + // if it's not a right click or a shift click it should be a left click (no shift) + // this is for when players are INSERTing items (i.e. item in hand and left clicking) + if (player.getItemInHand() == null && (!event.isRightClick() && !event.isShiftClick())) { + return; + } + + // Are they inserting a stack? + if (cursor != null && item.getType() == cursor.getType()) { + boolean enchantmentsEqual = areEnchantmentsEqual(item, cursor); + + // If they are clicking an item of the stack type, they are inserting it into the inventory, + // not switching it + // As long as the item isn't a degradable item, we can explicitly allow it if they have the same durability + if (item.getDurability() == cursor.getDurability() && item.getAmount() == cursor.getAmount() && enchantmentsEqual) { + return; + } + } + } + + // Attempt to load the protection at that location + Protection protection = lwc.findProtection(location); + + // If no protection was found we can safely ignore it + if (protection == null) { + return; + } + + // If it's not a donation chest, ignore if + if (protection.getType() != Protection.Type.DONATION) { + return; + } + + // Can they admin it? (remove items/etc) + boolean canAdmin = lwc.canAdminProtection(player, protection); + + // nope.avi + if (!canAdmin) { + event.setCancelled(true); + } + } + + /** + * Compares the enchantments on two item stacks and checks that they are equal (identical) + * + * @param stack1 + * @param stack2 + * @return + */ + private boolean areEnchantmentsEqual(ItemStack stack1, ItemStack stack2) { + if (stack1 == null || stack2 == null) { + return false; + } + + Map enchantments1 = stack1.getEnchantments(); + Map enchantments2 = stack2.getEnchantments(); + + if (enchantments1.size() != enchantments2.size()) { + return false; + } + + // Enchanted Books use ItemMeta + if (stack1.getItemMeta() != null && stack2.getItemMeta() != null) { + if (!stack1.getItemMeta().equals(stack2.getItemMeta())) { + return false; + } + } + + for (Enchantment enchantment : enchantments1.keySet()) { + if (!enchantments2.containsKey(enchantment)) { + return false; + } + + int level1 = enchantments1.get(enchantment); + int level2 = enchantments2.get(enchantment); + + if (level1 != level2) { + return false; + } + } + + return true; + } + +} diff --git a/core/src/main/java/com/griefcraft/listeners/LWCServerListener.java b/core/src/main/java/com/griefcraft/listeners/LWCServerListener.java new file mode 100644 index 000000000..4bbfca0ca --- /dev/null +++ b/core/src/main/java/com/griefcraft/listeners/LWCServerListener.java @@ -0,0 +1,58 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.listeners; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.lwc.LWCPlugin; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.plugin.Plugin; + +public class LWCServerListener implements Listener { + + private LWCPlugin plugin; + + public LWCServerListener(LWCPlugin plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPluginDisable(PluginDisableEvent event) { + if (!LWC.ENABLED) { + return; + } + + Plugin disabled = event.getPlugin(); + + // Removes any modules registered by the disabled plugin + plugin.getLWC().getModuleLoader().removeModules(disabled); + } + +} diff --git a/core/src/main/java/com/griefcraft/lwc/LWC.java b/core/src/main/java/com/griefcraft/lwc/LWC.java new file mode 100644 index 000000000..26d75e72a --- /dev/null +++ b/core/src/main/java/com/griefcraft/lwc/LWC.java @@ -0,0 +1,2046 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.lwc; + +import com.griefcraft.cache.ProtectionCache; +import com.griefcraft.integration.ICurrency; +import com.griefcraft.integration.IPermissions; +import com.griefcraft.integration.currency.BOSECurrency; +import com.griefcraft.integration.currency.EssentialsCurrency; +import com.griefcraft.integration.currency.NoCurrency; +import com.griefcraft.integration.currency.VaultCurrency; +import com.griefcraft.integration.currency.iConomy5Currency; +import com.griefcraft.integration.currency.iConomy6Currency; +import com.griefcraft.integration.permissions.BukkitPermissions; +import com.griefcraft.integration.permissions.PEXPermissions; +import com.griefcraft.integration.permissions.SuperPermsPermissions; +import com.griefcraft.integration.permissions.VaultPermissions; +import com.griefcraft.integration.permissions.bPermissions; +import com.griefcraft.io.BackupManager; +import com.griefcraft.listeners.LWCMCPCSupport; +import com.griefcraft.migration.ConfigPost300; +import com.griefcraft.migration.MySQLPost200; +import com.griefcraft.model.Flag; +import com.griefcraft.model.History; +import com.griefcraft.model.LWCPlayer; +import com.griefcraft.model.Permission; +import com.griefcraft.model.Protection; +import com.griefcraft.modules.admin.AdminBackup; +import com.griefcraft.modules.admin.AdminCache; +import com.griefcraft.modules.admin.AdminCleanup; +import com.griefcraft.modules.admin.AdminClear; +import com.griefcraft.modules.admin.AdminDump; +import com.griefcraft.modules.admin.AdminExpire; +import com.griefcraft.modules.admin.AdminFind; +import com.griefcraft.modules.admin.AdminFlush; +import com.griefcraft.modules.admin.AdminForceOwner; +import com.griefcraft.modules.admin.AdminLocale; +import com.griefcraft.modules.admin.AdminPurge; +import com.griefcraft.modules.admin.AdminPurgeBanned; +import com.griefcraft.modules.admin.AdminQuery; +import com.griefcraft.modules.admin.AdminRebuild; +import com.griefcraft.modules.admin.AdminReload; +import com.griefcraft.modules.admin.AdminRemove; +import com.griefcraft.modules.admin.AdminReport; +import com.griefcraft.modules.admin.AdminVersion; +import com.griefcraft.modules.admin.AdminView; +import com.griefcraft.modules.admin.BaseAdminModule; +import com.griefcraft.modules.confirm.ConfirmModule; +import com.griefcraft.modules.create.CreateModule; +import com.griefcraft.modules.credits.CreditsModule; +import com.griefcraft.modules.debug.DebugModule; +import com.griefcraft.modules.destroy.DestroyModule; +import com.griefcraft.modules.doors.DoorsModule; +import com.griefcraft.modules.fix.FixModule; +import com.griefcraft.modules.flag.BaseFlagModule; +import com.griefcraft.modules.flag.MagnetModule; +import com.griefcraft.modules.free.FreeModule; +import com.griefcraft.modules.history.HistoryModule; +import com.griefcraft.modules.info.InfoModule; +import com.griefcraft.modules.limits.LimitsModule; +import com.griefcraft.modules.limits.LimitsV2; +import com.griefcraft.modules.modes.BaseModeModule; +import com.griefcraft.modules.modes.DropTransferModule; +import com.griefcraft.modules.modes.NoSpamModule; +import com.griefcraft.modules.modes.PersistModule; +import com.griefcraft.modules.modify.ModifyModule; +import com.griefcraft.modules.owners.OwnersModule; +import com.griefcraft.modules.pluginsupport.Towny; +import com.griefcraft.modules.pluginsupport.WorldGuard; +import com.griefcraft.modules.redstone.RedstoneModule; +import com.griefcraft.modules.setup.BaseSetupModule; +import com.griefcraft.modules.setup.DatabaseSetupModule; +import com.griefcraft.modules.setup.LimitsSetup; +import com.griefcraft.modules.unlock.UnlockModule; +import com.griefcraft.scripting.Module; +import com.griefcraft.scripting.ModuleLoader; +import com.griefcraft.scripting.event.LWCAccessEvent; +import com.griefcraft.scripting.event.LWCReloadEvent; +import com.griefcraft.scripting.event.LWCSendLocaleEvent; +import com.griefcraft.sql.Database; +import com.griefcraft.sql.PhysDB; +import com.griefcraft.util.Colors; +import com.griefcraft.util.DatabaseThread; +import com.griefcraft.util.ProtectionFinder; +import com.griefcraft.util.Statistics; +import com.griefcraft.util.StringUtil; +import com.griefcraft.util.UUIDRegistry; +import com.griefcraft.util.config.Configuration; +import com.griefcraft.util.locale.LocaleUtil; +import com.griefcraft.util.matchers.DoubleChestMatcher; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +import org.mcstats.Metrics; + +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class LWC { + + /** + * If LWC is currently enabled + */ + public static boolean ENABLED = false; + + /** + * The current instance of LWC + */ + private static LWC instance; + + /** + * Core LWC configuration + */ + private Configuration configuration; + + /** + * The module loader + */ + private final ModuleLoader moduleLoader; + + /** + * The manager of backups + */ + private final BackupManager backupManager; + + /** + * The protection cache + */ + private final ProtectionCache protectionCache; + + /** + * Physical database instance + */ + private PhysDB physicalDatabase; + + /** + * Plugin instance + */ + private LWCPlugin plugin; + + /** + * Updates to the database that can be ran on a seperate thread + */ + private DatabaseThread databaseThread; + + /** + * The permissions handler + */ + private IPermissions permissions; + + /** + * The currency handler + */ + private ICurrency currency; + + /** + * Protection configuration cache + */ + private final Map protectionConfigurationCache = new HashMap(); + + public LWC(LWCPlugin plugin) { + this.plugin = plugin; + LWC.instance = this; + configuration = Configuration.load("core.yml"); + protectionCache = new ProtectionCache(this); + backupManager = new BackupManager(); + moduleLoader = new ModuleLoader(this); + } + + /** + * Get the currently loaded LWC instance + * + * @return + */ + public static LWC getInstance() { + return instance; + } + + /** + * Get a string representation of a block type + * + * @param id + * @return + */ + public static String materialToString(int id) { + return materialToString(Material.getMaterial(id)); + } + + /** + * Get a string representation of a block material + * + * @param material + * @return + */ + public static String materialToString(Material material) { + if (material != null) { + String materialName = normalizeMaterialName(material); + + // attempt to match the locale + String locale = LWC.getInstance().getPlugin().getMessageParser().parseMessage(materialName.toLowerCase()); + + // if it starts with UNKNOWN_LOCALE, use the default material name + if (locale == null) { + locale = materialName; + } + + return StringUtil.capitalizeFirstLetter(locale); + } + + return ""; + } + + /** + * Normalize a name to a more readable & usable form. + *

+ * E.g sign_post/wall_sign = Sign, furnace/burning_furnace = Furnace, + * iron_door_block = iron_door + * + * @param material + * @return + */ + public static String normalizeMaterialName(Material material) { + String name = StringUtils.replace(material.toString().toLowerCase(), "block", ""); + + // some name normalizations + if (name.contains("sign")) { + name = "Sign"; + } + + if (name.contains("furnace")) { + name = "furnace"; + } + + if (name.endsWith("_")) { + name = name.substring(0, name.length() - 1); + } + + return name.toLowerCase(); + } + + /** + * Restore the direction the block is facing for when 1.8 broke it + * + * @param block + */ + public void adjustChestDirection(Block block, BlockFace face) { + if (block.getType() != Material.CHEST) { + return; + } + + // Is there a double chest? + Block doubleChest = findAdjacentDoubleChest(block); + + // Calculate the data byte to set + byte data = 0; + + switch (face) { + case NORTH: + data = 4; + break; + + case SOUTH: + data = 5; + break; + + case EAST: + data = 2; + break; + + case WEST: + data = 3; + break; + } + + // set the data for both sides of the chest + block.setData(data); + + if (doubleChest != null) { + doubleChest.setData(data); + } + } + + /** + * Look for a double chest adjacent to a chest + * + * @param block + * @return + */ + public Block findAdjacentDoubleChest(Block block) { + if (!DoubleChestMatcher.PROTECTABLES_CHESTS.contains(block.getType())) { + throw new UnsupportedOperationException("findAdjacentDoubleChest() cannot be called on a: " + block.getType()); + } + + return findAdjacentBlock(block, block.getType()); + } + + /** + * Check if a player has the ability to access a protection + * + * @param player + * @param block + * @return + */ + public boolean canAccessProtection(Player player, Block block) { + Protection protection = findProtection(block.getLocation()); + + return protection != null && canAccessProtection(player, protection); + } + + /** + * Check if a player has the ability to access a protection + * + * @param player + * @param x + * @param y + * @param z + * @return + */ + public boolean canAccessProtection(Player player, int x, int y, int z) { + return canAccessProtection(player, physicalDatabase.loadProtection(player.getWorld().getName(), x, y, z)); + } + + /** + * Check if a player has the ability to administrate a protection + * + * @param player + * @param block + * @return + */ + public boolean canAdminProtection(Player player, Block block) { + Protection protection = findProtection(block.getLocation()); + + return protection != null && canAdminProtection(player, protection); + } + + /** + * Check if a player has the ability to administrate a protection + * + * @param player + * @param protection + * @return + */ + public boolean canAdminProtection(Player player, Protection protection) { + if (protection == null || player == null) { + return true; + } + + if (isAdmin(player)) { + return true; + } + + // Their access level + Permission.Access access = Permission.Access.NONE; + + switch (protection.getType()) { + case PUBLIC: + if (protection.isOwner(player)) { + return true; + } + + break; + + case PASSWORD: + if (protection.isOwner(player) && wrapPlayer(player).getAccessibleProtections().contains(protection)) { + return true; + } + + break; + + case PRIVATE: + case DONATION: + if (protection.isOwner(player)) { + return true; + } + + if (protection.getAccess(player.getUniqueId().toString(), Permission.Type.PLAYER) == Permission.Access.ADMIN) { + return true; + } + + if (protection.getAccess(player.getName(), Permission.Type.PLAYER) == Permission.Access.ADMIN) { + return true; + } + + for (String groupName : permissions.getGroups(player)) { + if (protection.getAccess(groupName, Permission.Type.GROUP) == Permission.Access.ADMIN) { + return true; + } + } + + break; + } + + // call the canAccessProtection hook + LWCAccessEvent event = new LWCAccessEvent(player, protection, access); + moduleLoader.dispatchEvent(event); + + return event.getAccess() == Permission.Access.ADMIN; + } + + /** + * Deposit items into an inventory chest + * Works with double chests. + * + * @param block + * @param itemStack + * @return remaining items (if any) + */ + public Map depositItems(Block block, ItemStack itemStack) { + BlockState blockState; + + if ((blockState = block.getState()) != null && (blockState instanceof InventoryHolder)) { + Block doubleChestBlock = null; + InventoryHolder holder = (InventoryHolder) blockState; + + if (DoubleChestMatcher.PROTECTABLES_CHESTS.contains(block.getType())) { + doubleChestBlock = findAdjacentDoubleChest(block); + } else if (block.getType() == Material.FURNACE || block.getType() == Material.BURNING_FURNACE) { + Inventory inventory = holder.getInventory(); + + if (inventory.getItem(0) != null && inventory.getItem(1) != null) { + if (inventory.getItem(0).getType() == itemStack.getType() + && inventory.getItem(0).getData().getData() == itemStack.getData().getData() + && inventory.getItem(0).getMaxStackSize() >= (inventory.getItem(0).getAmount() + itemStack.getAmount())) { + // ItemStack fits on Slot 0 + } else if (inventory.getItem(1).getType() == itemStack.getType() + && inventory.getItem(1).getData().getData() == itemStack.getData().getData() + && inventory.getItem(1).getMaxStackSize() >= (inventory.getItem(1).getAmount() + itemStack.getAmount())) { + // ItemStack fits on Slot 1 + } else { + return null; + } + } + } + + if (itemStack.getAmount() <= 0) { + return new HashMap(); + } + + Map remaining = holder.getInventory().addItem(itemStack); + + // we have remainders, deal with it + if (remaining.size() > 0) { + int key = remaining.keySet().iterator().next(); + ItemStack remainingItemStack = remaining.get(key); + + // is it a double chest ????? + if (doubleChestBlock != null) { + InventoryHolder holder2 = (InventoryHolder) doubleChestBlock.getState(); + remaining = holder2.getInventory().addItem(remainingItemStack); + } + + // recheck remaining in the event of double chest being used + if (remaining.size() > 0) { + return remaining; + } + } + } + + return new HashMap(); + } + + /** + * Find a block that is adjacent to another block given a Material + * + * @param block + * @param material + * @param ignore + * @return + */ + public Block findAdjacentBlock(Block block, Material material, Block... ignore) { + BlockFace[] faces = new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}; + List ignoreList = Arrays.asList(ignore); + + for (BlockFace face : faces) { + Block adjacentBlock = block.getRelative(face); + + if (adjacentBlock.getType() == material && !ignoreList.contains(adjacentBlock)) { + return adjacentBlock; + } + } + + return null; + } + + /** + * Find a block that is adjacent to another block on any of the block's 6 sides given a Material + * + * @param block + * @param material + * @param ignore + * @return + */ + public Block findAdjacentBlockOnAllSides(Block block, Material material, Block... ignore) { + BlockFace[] faces = new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN}; + List ignoreList = Arrays.asList(ignore); + + for (BlockFace face : faces) { + Block adjacentBlock = block.getRelative(face); + + if (adjacentBlock.getType() == material && !ignoreList.contains(adjacentBlock)) { + return adjacentBlock; + } + } + + return null; + } + + /** + * Find a protection that is adjacent to another block on any of the block's 6 sides + * + * @param block + * @param ignore + * @return + */ + public List findAdjacentProtectionsOnAllSides(Block block, Block... ignore) { + BlockFace[] faces = new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN}; + List ignoreList = Arrays.asList(ignore); + List found = new ArrayList(); + + for (BlockFace face : faces) { + Protection protection; + Block adjacentBlock = block.getRelative(face); + + if (!ignoreList.contains(adjacentBlock.getLocation()) && (protection = findProtection(adjacentBlock.getLocation())) != null) { + found.add(protection); + } + } + + return found; + } + + /** + * Free some memory (LWC was disabled) + */ + public void destruct() { + // destroy the modules + moduleLoader.shutdown(); + + log("Flushing protection updates (" + databaseThread.size() + ")"); + + if (databaseThread != null) { + databaseThread.stop(); + databaseThread = null; + } + + if (physicalDatabase != null) { + physicalDatabase.dispose(); + } + + physicalDatabase = null; + } + + /** + * Log a string + * + * @param str + */ + public void log(String str) { + plugin.getLogger().info(str); + } + + /** + * Encrypt a string using SHA1 + * + * @param text + * @return + */ + public String encrypt(String text) { + return StringUtil.encrypt(text); + } + + /** + * Enforce access to a protected block + * + * @param player + * @param protection + * @param block + * @return true if the player was granted access + */ + public boolean enforceAccess(Player player, Protection protection, Block block, boolean hasAccess) { + MessageParser parser = plugin.getMessageParser(); + + if (block == null || protection == null) { + return true; + } + + // support for old protection dbs that do not contain the block id + if (block != null && (protection.getBlockId() <= 0 && block.getTypeId() != protection.getBlockId())) { + protection.setBlockId(block.getTypeId()); + protection.save(); + } + + // multi-world, update old protections + if (block != null && (protection.getWorld() == null || !block.getWorld().getName().equals(protection.getWorld()))) { + protection.setWorld(block.getWorld().getName()); + protection.save(); + } + + // update timestamp + if (hasAccess) { + long timestamp = System.currentTimeMillis() / 1000L; + + // check that they aren't an admin and if they are, they need to be the owner of the protection or have access through /cmodify + if (protection.isOwner(player) || protection.getAccess(player.getName(), Permission.Type.PLAYER) != Permission.Access.NONE) { + protection.setLastAccessed(timestamp); + protection.save(); + } + } + + boolean permShowNotices = hasPermission(player, "lwc.shownotices"); + if ((permShowNotices && configuration.getBoolean("core.showNotices", true)) + && !Boolean.parseBoolean(resolveProtectionConfiguration(block, "quiet"))) { + boolean isOwner = protection.isOwner(player); + boolean showMyNotices = configuration.getBoolean("core.showMyNotices", true); + + if (!isOwner || (isOwner && (showMyNotices || permShowNotices))) { + String owner; + + // replace your username with "you" if you own the protection + if (protection.isRealOwner(player)) { + owner = parser.parseMessage("you"); + } else { + owner = protection.getFormattedOwnerPlayerName(); + } + + String blockName = materialToString(block); + String protectionTypeToString = parser.parseMessage(protection.typeToString().toLowerCase()); + + if (protectionTypeToString == null) { + protectionTypeToString = "Unknown"; + } + + if (parser.parseMessage("protection." + blockName.toLowerCase() + ".notice.protected") != null) { + sendLocale(player, "protection." + blockName.toLowerCase() + ".notice.protected", "type", protectionTypeToString, "block", blockName, "owner", owner); + } else { + sendLocale(player, "protection.general.notice.protected", "type", protectionTypeToString, "block", blockName, "owner", owner); + } + } + } + + if (!hasAccess) { + Protection.Type type = protection.getType(); + + if (type == Protection.Type.PASSWORD) { + sendLocale(player, "protection.general.locked.password", "block", materialToString(block), "owner", protection.getOwner()); + } else if (type == Protection.Type.PRIVATE || type == Protection.Type.DONATION) { + sendLocale(player, "protection.general.locked.private", "block", materialToString(block), "owner", protection.getOwner()); + } + } + + return hasAccess; + } + + /** + * Check if a player has the ability to access a protection + * + * @param player + * @param protection + * @return + */ + public boolean canAccessProtection(Player player, Protection protection) { + if (protection == null || player == null) { + return true; + } + + if (isAdmin(player)) { + return true; + } + + if (isMod(player)) { + Player protectionOwner = protection.getBukkitOwner(); + + if (protectionOwner == null) { + return true; + } + + if (!isAdmin(protectionOwner)) { + return true; + } + } + + // Their access level + Permission.Access access = Permission.Access.NONE; + + switch (protection.getType()) { + case PUBLIC: + case DONATION: + return true; + + case PASSWORD: + if (wrapPlayer(player).getAccessibleProtections().contains(protection)) { + return true; + } + + break; + + case PRIVATE: + if (protection.isOwner(player)) { + return true; + } + + if (protection.getAccess(player.getUniqueId().toString(), Permission.Type.PLAYER).ordinal() >= Permission.Access.PLAYER.ordinal()) { + return true; + } + + if (protection.getAccess(player.getName(), Permission.Type.PLAYER).ordinal() >= Permission.Access.PLAYER.ordinal()) { + return true; + } + + // Check for item keys + for (Permission permission : protection.getPermissions()) { + if (permission.getType() != Permission.Type.ITEM) { + continue; + } + + // Get the item they need to have + int item = Integer.parseInt(permission.getName()); + + // Are they wielding it? + if (player.getItemInHand().getTypeId() == item) { + return true; + } + } + + for (String groupName : permissions.getGroups(player)) { + if (protection.getAccess(groupName, Permission.Type.GROUP).ordinal() >= Permission.Access.PLAYER.ordinal()) { + return true; + } + } + + break; + } + + // call the canAccessProtection hook + LWCAccessEvent event = new LWCAccessEvent(player, protection, access); + moduleLoader.dispatchEvent(event); + + return event.getAccess() == Permission.Access.PLAYER || event.getAccess() == Permission.Access.ADMIN; + } + + /** + * Check if a player can do mod functions on LWC + * + * @param player the player to check + * @return true if the player is an LWC mod + */ + public boolean isMod(Player player) { + return hasPermission(player, "lwc.mod"); + } + + /** + * Check if a player can do admin functions on LWC + * + * @param player the player to check + * @return true if the player is an LWC admin + */ + public boolean isAdmin(Player player) { + if (player.isOp()) { + if (configuration.getBoolean("core.opIsLWCAdmin", true)) { + return true; + } + } + + return hasPermission(player, "lwc.admin"); + } + + /** + * Check if a player has a permissions node + * + * @param player + * @param node + * @return + */ + public boolean hasPermission(Player player, String node) { + try { + return player.hasPermission(node); + } catch (NoSuchMethodError e) { + // their server does not support Superperms.. + return !node.contains("admin") && !node.contains("mod"); + } + } + + /** + * Create an LWCPlayer object for a player + * + * @param sender + * @return + */ + public LWCPlayer wrapPlayer(CommandSender sender) { + if (sender instanceof LWCPlayer) { + return (LWCPlayer) sender; + } + + if (!(sender instanceof Player)) { + return null; + } + + return LWCPlayer.getPlayer((Player) sender); + } + + /** + * Find a player in the given ranges + * + * @param minX + * @param maxX + * @param minY + * @param maxY + * @param minZ + * @param maxZ + * @return + */ + public Player findPlayer(int minX, int maxX, int minY, int maxY, int minZ, int maxZ) { + for (Player player : plugin.getServer().getOnlinePlayers()) { + Location location = player.getLocation(); + int plrX = location.getBlockX(); + int plrY = location.getBlockY(); + int plrZ = location.getBlockZ(); + + // simple check of the ranges + if (plrX >= minX && plrX <= maxX && plrY >= plrY && plrY <= maxY && plrZ >= minZ && plrZ <= maxZ) { + return player; + } + } + + return null; + } + + /** + * Send a locale to a player or console + * + * @param sender + * @param key + * @param args + */ + public void sendLocale(CommandSender sender, String key, Object... args) { + String[] message; // The message to send to the player + MessageParser parser = plugin.getMessageParser(); + String parsed = parser.parseMessage(key, args); + + if (parsed == null) { + return; // Nothing to send + } + + // message = parsed.split("\\n"); + message = StringUtils.split(parsed, '\n'); + + // broadcast an event if they are a player + if (sender instanceof Player) { + LWCSendLocaleEvent evt = new LWCSendLocaleEvent((Player) sender, key); + moduleLoader.dispatchEvent(evt); + + // did they cancel it? + if (evt.isCancelled()) { + return; + } + } + + if (message == null) { + sender.sendMessage(Colors.Red + "LWC: " + Colors.White + "Undefined locale: \"" + Colors.Gray + key + Colors.White + "\""); + return; + } + + if (message.length > 0 && message[0].equalsIgnoreCase("null")) { + return; + } + + // Send the message! + // sender.sendMessage(message); + for (String line : message) { + sender.sendMessage(line); + } + } + + /** + * Get a string representation of a block's material + * + * @param block + * @return + */ + public static String materialToString(Block block) { + return materialToString(block.getType()); + } + + /** + * Fast remove all protections for a player. ~100k protections / second. + * + * @param sender + * @param player + * @param shouldRemoveBlocks + * @return + */ + public int fastRemoveProtectionsByPlayer(CommandSender sender, String player, boolean shouldRemoveBlocks) { + // remove their protections first + int ret = fastRemoveProtections(sender, "Lower(owner) = Lower('" + player + "')", shouldRemoveBlocks); + + // invalid any history objects associated with the player + physicalDatabase.invalidateHistory(player); + + return ret; + } + + /** + * Remove protections very quickly with raw SQL calls + * + * @param sender + * @param where + * @param shouldRemoveBlocks + * @return + */ + public int fastRemoveProtections(CommandSender sender, String where, boolean shouldRemoveBlocks) { + List exemptedBlocks = configuration.getIntList("optional.exemptBlocks", new ArrayList()); + List toRemove = new LinkedList(); + List removeBlocks = null; + int totalProtections = physicalDatabase.getProtectionCount(); + int completed = 0; + int count = 0; + + // flush all changes to the database before working on the live database + databaseThread.flush(); + + if (shouldRemoveBlocks) { + removeBlocks = new LinkedList(); + } + + if (where != null && !where.trim().isEmpty()) { + where = " WHERE " + where.trim(); + } + + sender.sendMessage("Loading protections via STREAM mode"); + + try { + Statement resultStatement = physicalDatabase.getConnection().createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + + if (physicalDatabase.getType() == Database.Type.MySQL) { + resultStatement.setFetchSize(Integer.MIN_VALUE); + } + + String prefix = physicalDatabase.getPrefix(); + ResultSet result = resultStatement.executeQuery("SELECT id, owner, type, x, y, z, data, blockId, world, password, date, last_accessed FROM " + prefix + "protections" + where); + + while (result.next()) { + Protection protection = physicalDatabase.resolveProtection(result); + World world = protection.getBukkitWorld(); + + // check if the protection is exempt from being removed + if (protection.hasFlag(Flag.Type.EXEMPTION) || exemptedBlocks.contains(protection.getBlockId())) { + continue; + } + + count++; + + if (count % 100000 == 0 || count == totalProtections || count == 1) { + sender.sendMessage(Colors.Red + count + " / " + totalProtections); + } + + if (world == null) { + continue; + } + + // remove the protection + toRemove.add(protection.getId()); + + // remove the block ? + if (shouldRemoveBlocks) { + removeBlocks.add(protection.getBlock()); + } + + // Remove it from the cache if it's in there + Protection cached = protectionCache.getProtection(protection.getCacheKey()); + if (cached != null) { + cached.removeCache(); + } + + completed++; + } + + // Close the streaming statement + result.close(); + resultStatement.close(); + + // flush all of the queries + fullRemoveProtections(sender, toRemove); + + if (shouldRemoveBlocks) { + removeBlocks(sender, removeBlocks); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return completed; + } + + /** + * Push removal changes to the database + * + * @param sender + * @param toRemove + */ + private void fullRemoveProtections(CommandSender sender, List toRemove) throws SQLException { + StringBuilder deleteProtectionsQuery = new StringBuilder(); + StringBuilder deleteHistoryQuery = new StringBuilder(); + int total = toRemove.size(); + int count = 0; + + // iterate over the items to remove + Iterator iter = toRemove.iterator(); + + // the database prefix + String prefix = getPhysicalDatabase().getPrefix(); + + // create the statement to use + Statement statement = getPhysicalDatabase().getConnection().createStatement(); + + while (iter.hasNext()) { + int protectionId = iter.next(); + + if (count % 10000 == 0) { + deleteProtectionsQuery.append("DELETE FROM ").append(prefix).append("protections WHERE id IN (").append(protectionId); + deleteHistoryQuery.append("UPDATE ").append(prefix).append("history SET status = " + History.Status.INACTIVE.ordinal() + " WHERE protectionId IN(").append(protectionId); + } else { + deleteProtectionsQuery.append(",").append(protectionId); + deleteHistoryQuery.append(",").append(protectionId); + } + + if (count % 10000 == 9999 || count == (total - 1)) { + deleteProtectionsQuery.append(")"); + deleteHistoryQuery.append(")"); + statement.executeUpdate(deleteProtectionsQuery.toString()); + statement.executeUpdate(deleteHistoryQuery.toString()); + deleteProtectionsQuery.setLength(0); + deleteHistoryQuery.setLength(0); + + sender.sendMessage(Colors.Green + "REMOVED " + (count + 1) + " / " + total); + } + + count++; + physicalDatabase.decrementProtectionCount(); + } + + statement.close(); + } + + /** + * Remove a list of blocks from the world + * + * @param sender + * @param blocks + */ + private void removeBlocks(CommandSender sender, List blocks) { + int count = 0; + + for (Block block : blocks) { + if (block == null || !isProtectable(block)) { + continue; + } + + // possibility of a double chest + if (DoubleChestMatcher.PROTECTABLES_CHESTS.contains(block.getType())) { + Block doubleChest = findAdjacentDoubleChest(block); + + if (doubleChest != null) { + removeInventory(doubleChest); + doubleChest.setType(Material.AIR); + } + } + + // remove the inventory from the block if it has one + removeInventory(block); + + // and now remove the block + block.setType(Material.AIR); + + count++; + } + + sender.sendMessage("Removed " + count + " blocks from the world"); + } + + /** + * Remove the inventory from a block + * + * @param block + */ + private void removeInventory(Block block) { + if (block == null) { + return; + } + + if (!(block.getState() instanceof InventoryHolder)) { + return; + } + + InventoryHolder holder = (InventoryHolder) block.getState(); + holder.getInventory().clear(); + } + + /** + * Compares two blocks if they are equal + * + * @param block + * @param block2 + * @return + */ + public boolean blockEquals(Block block, Block block2) { + return block.getType() == block2.getType() && block.getX() == block2.getX() && block.getY() == block2.getY() + && block.getZ() == block2.getZ() && block.getData() == block2.getData(); + } + + /** + * Compares two blocks if they are equal + * + * @param block + * @param block2 + * @return + */ + public boolean blockEquals(BlockState block, BlockState block2) { + return block.getType() == block2.getType() && block.getX() == block2.getX() && block.getY() == block2.getY() + && block.getZ() == block2.getZ() && block.getRawData() == block2.getRawData(); + } + + /** + * Find a protection linked to the location + * + * @param location + * @return + */ + public Protection findProtection(Location location) { + String cacheKey = protectionCache.cacheKey(location); + + if (protectionCache.isKnownNull(cacheKey)) { + return null; + } + + Protection protection = protectionCache.getProtection(cacheKey); + + return protection != null ? protection : findProtection(location.getBlock()); + } + + /** + * Find a protection linked to the block + * + * @param block + * @return + */ + public Protection findProtection(Block block) { + return findProtection(block.getState()); + } + + /** + * Find a protection linked to the block + * + * @param state + * @return + */ + public Protection findProtection(BlockState state) { + // If the block type is AIR, then we have a problem .. but attempt to load a protection anyway + // Note: this call stems from a very old bug in Bukkit that likely does not exist anymore at all + // but is kept just incase. At one point getBlock() in Bukkit would sometimes say a block + // is an eir block even though the client and server sees it differently (ie a chest). + // This was of course very problematic! + if (state.getType() == Material.AIR) { + // We won't be able to match any other blocks anyway, so the least we can do is attempt to load a protection + return physicalDatabase.loadProtection(state.getWorld().getName(), state.getX(), state.getY(), state.getZ()); + } + + // Create a protection finder + ProtectionFinder finder = new ProtectionFinder(this); + + // Search for a protection + boolean result = finder.matchBlocks(state); + + Protection found = null; + + // We're done, load the possibly loaded protection + if (result) { + found = finder.loadProtection(); + } + + if (found == null) { + protectionCache.addKnownNull(protectionCache.cacheKey(state.getLocation())); + } + + return found; + } + + /** + * Find a protection linked to the block at [x, y, z] + * + * @param world + * @param x + * @param y + * @param z + * @return + */ + public Protection findProtection(World world, int x, int y, int z) { + if (world == null) { + return null; + } + + return findProtection(new Location(world, x, y, z)); + } + + /** + * Check if a player has either access to lwc.admin or the specified node + * + * @param sender + * @param node + * @return + */ + public boolean hasAdminPermission(CommandSender sender, String node) { + return isAdmin(sender) || hasPermission(sender, node, "lwc.admin"); + } + + /** + * Check if a player is an LWC admin -- Console defaults to *YES* + * + * @param sender + * @return + */ + public boolean isAdmin(CommandSender sender) { + return !(sender instanceof Player) || isAdmin((Player) sender); + } + + /** + * Check a player for a node, using a fallback as a default (e.g lwc.protect) + * + * @param sender + * @param node + * @param fallback + * @return + */ + public boolean hasPermission(CommandSender sender, String node, String... fallback) { + if (!(sender instanceof Player)) { + return true; + } + + Player player = (Player) sender; + boolean hasNode = hasPermission(player, node); + + if (!hasNode) { + for (String temp : fallback) { + if (hasPermission(player, temp)) { + return true; + } + } + } + + return hasNode; + } + + /** + * Check if a player has either access to lwc.protect or the specified node + * + * @param sender + * @param node + * @return + */ + public boolean hasPlayerPermission(CommandSender sender, String node) { + return hasPermission(sender, node, "lwc.protect"); + } + + /** + * Check if a mode is enabled + * + * @param mode + * @return + */ + public boolean isModeEnabled(String mode) { + return configuration.getBoolean("modes." + mode + ".enabled", true); + } + + /** + * Check if a mode is whitelisted for a player + * + * @param mode + * @return + */ + public boolean isModeWhitelisted(Player player, String mode) { + return hasPermission(player, "lwc.mode." + mode, "lwc.allmodes"); + } + + /** + * Check a block to see if it is protectable + * + * @param block + * @return + */ + public boolean isProtectable(Block block) { + Material material = block.getType(); + + if (material == null) { + return false; + } + + return Boolean.parseBoolean(resolveProtectionConfiguration(block, "enabled")); + } + + /** + * Check a block to see if it is protectable + * + * @param state + * @return + */ + public boolean isProtectable(BlockState state) { + Material material = state.getType(); + + if (material == null) { + return false; + } + + return Boolean.parseBoolean(resolveProtectionConfiguration(state, "enabled")); + } + + /** + * Get the appropriate config value for the block (protections.block.node) + * + * @param block + * @param node + * @return + */ + public String resolveProtectionConfiguration(Block block, String node) { + Material material = block.getType(); + String cacheKey = block.getData() + "-" + material.toString() + "-" + node; + if (protectionConfigurationCache.containsKey(cacheKey)) { + return protectionConfigurationCache.get(cacheKey); + } + + List names = new ArrayList(); + + String materialName = normalizeMaterialName(material); + + // add the name & the block id + names.add(materialName); + names.add(material.getId() + ""); + names.add(material.getId() + ":" + block.getData()); + names.add(materialName + ":" + block.getData()); + + if (!materialName.equals(material.toString().toLowerCase())) { + names.add(material.toString().toLowerCase()); + } + + // Add the wildcards last so it can be overriden + names.add("*"); + names.add(material.getId() + ":*"); + + String value = configuration.getString("protections." + node); + + for (String name : names) { + String temp = configuration.getString("protections.blocks." + name + "." + node); + + if (temp != null && !temp.isEmpty()) { + value = temp; + } + } + + protectionConfigurationCache.put(cacheKey, value); + return value; + } + + /** + * Get the appropriate config value for the block (protections.block.node) + * + * @param state + * @param node + * @return + */ + public String resolveProtectionConfiguration(BlockState state, String node) { + Material material = state.getType(); + String cacheKey = state.getRawData() + "-" + material.toString() + "-" + node; + if (protectionConfigurationCache.containsKey(cacheKey)) { + return protectionConfigurationCache.get(cacheKey); + } + + List names = new ArrayList(); + + String materialName = normalizeMaterialName(material); + + // add the name & the block id + names.add(materialName); + names.add(material.getId() + ""); + names.add(material.getId() + ":" + state.getRawData()); + names.add(materialName + ":" + state.getRawData()); + + if (!materialName.equals(material.toString().toLowerCase())) { + names.add(material.toString().toLowerCase()); + } + + // Add the wildcards last so it can be overriden + names.add("*"); + names.add(material.getId() + ":*"); + + String value = configuration.getString("protections." + node); + + for (String name : names) { + String temp = configuration.getString("protections.blocks." + name + "." + node); + + if (temp != null && !temp.isEmpty()) { + value = temp; + } + } + + protectionConfigurationCache.put(cacheKey, value); + return value; + } + + /** + * Get the appropriate config value for the block (protections.block.node) + * + * @param material + * @param node + * @return + */ + public String resolveProtectionConfiguration(Material material, String node) { + String cacheKey = "00-" + material.toString() + "-" + node; + if (protectionConfigurationCache.containsKey(cacheKey)) { + return protectionConfigurationCache.get(cacheKey); + } + + List names = new ArrayList(); + + String materialName = normalizeMaterialName(material); + + // add the name & the block id + names.add(materialName); + names.add(material.getId() + ""); + + if (!materialName.equals(material.toString().toLowerCase())) { + names.add(material.toString().toLowerCase()); + } + + // Add the wildcards last so it can be overriden + names.add("*"); + names.add(material.getId() + ":*"); + + String value = configuration.getString("protections." + node); + + for (String name : names) { + String temp = configuration.getString("protections.blocks." + name + "." + node); + + if (temp != null && !temp.isEmpty()) { + value = temp; + } + } + + protectionConfigurationCache.put(cacheKey, value); + return value; + } + + /** + * Load sqlite (done only when LWC is loaded so memory isn't used unnecessarily) + */ + public void load() { + configuration = Configuration.load("core.yml"); + registerCoreModules(); + + // check for upgrade before everything else + new ConfigPost300().run(); + plugin.loadDatabase(); + + Statistics.init(); + + physicalDatabase = new PhysDB(); + databaseThread = new DatabaseThread(this); + + // Permissions init + permissions = new SuperPermsPermissions(); + + if (resolvePlugin("Vault") != null) { + permissions = new VaultPermissions(); + } else if (resolvePlugin("PermissionsBukkit") != null) { + permissions = new BukkitPermissions(); + } else if (resolvePlugin("PermissionsEx") != null) { + permissions = new PEXPermissions(); + } else if (resolvePlugin("bPermissions") != null) { + permissions = new bPermissions(); + } + + // Currency init + currency = new NoCurrency(); + + if (resolvePlugin("Vault") != null) { + currency = new VaultCurrency(); + } else if (resolvePlugin("iConomy") != null) { + // We need to figure out which iConomy plugin we have... + Plugin plugin = resolvePlugin("iConomy"); + + // get the class name + String className = plugin.getClass().getName(); + + // check for the iConomy5 package + try { + if (className.startsWith("com.iConomy")) { + currency = new iConomy5Currency(); + } else { + // iConomy 6! + currency = new iConomy6Currency(); + } + } catch (NoClassDefFoundError e) { + } + } else if (resolvePlugin("BOSEconomy") != null) { + currency = new BOSECurrency(); + } else if (resolvePlugin("Essentials") != null) { + currency = new EssentialsCurrency(); + } + + plugin.getUpdater().init(); + + log("Connecting to " + Database.DefaultType); + try { + if (!physicalDatabase.connect()) { + Bukkit.getPluginManager().disablePlugin(plugin); + return; + } + physicalDatabase.load(); + } catch (Exception e) { + e.printStackTrace(); + } + + // check any major conversions + new MySQLPost200().run(); + + // precache protections + physicalDatabase.precache(); + + // We are now done loading! + moduleLoader.loadAll(); + + // Should we try metrics? + if (!configuration.getBoolean("optional.optOut", false)) { + try { + Metrics metrics = new Metrics(plugin); + + // Create a line graph + Metrics.Graph lineGraph = metrics.createGraph("Protections"); + + // Add the total protections plotter + lineGraph.addPlotter(new Metrics.Plotter("Total") { + @Override + public int getValue() { + return physicalDatabase.getProtectionCount(); + } + }); + + // Create a pie graph for individual protections + Metrics.Graph pieGraph = metrics.createGraph("Protection percentages"); + + for (final Protection.Type type : Protection.Type.values()) { + if (type == Protection.Type.RESERVED1 || type == Protection.Type.RESERVED2) { + continue; + } + + // Create the plotter + Metrics.Plotter plotter = new Metrics.Plotter(StringUtil.capitalizeFirstLetter(type.toString()) + " Protections") { + @Override + public int getValue() { + return physicalDatabase.getProtectionCount(type); + } + }; + + // Add it to both graphs + lineGraph.addPlotter(plotter); + pieGraph.addPlotter(plotter); + } + + // Locale + Metrics.Graph langGraph = metrics.createGraph("Locale"); + langGraph.addPlotter(new Metrics.Plotter(LocaleUtil.iso639ToEnglish(configuration.getString("core.locale", "en"))) { + @Override + public int getValue() { + return 1; + } + }); + + // Database type + Metrics.Graph databaseGraph = metrics.createGraph("Database Engine"); + databaseGraph.addPlotter(new Metrics.Plotter(physicalDatabase.getType().toString()) { + @Override + public int getValue() { + return 1; + } + }); + + metrics.start(); + } catch (IOException e) { + log(e.getMessage()); + } + } + } + + /** + * Register the core modules for LWC + */ + private void registerCoreModules() { + // MCPC + registerModule(new LWCMCPCSupport(this)); + + // core + registerModule(new LimitsV2()); + registerModule(new LimitsModule()); + registerModule(new CreateModule()); + registerModule(new ModifyModule()); + registerModule(new DestroyModule()); + registerModule(new FreeModule()); + registerModule(new InfoModule()); + registerModule(new UnlockModule()); + registerModule(new OwnersModule()); + registerModule(new DoorsModule()); + registerModule(new DebugModule()); + registerModule(new CreditsModule()); + registerModule(new FixModule()); + registerModule(new HistoryModule()); + registerModule(new ConfirmModule()); + + // admin commands + registerModule(new BaseAdminModule()); + registerModule(new AdminCache()); + registerModule(new AdminCleanup()); + registerModule(new AdminClear()); + registerModule(new AdminFind()); + registerModule(new AdminFlush()); + registerModule(new AdminForceOwner()); + registerModule(new AdminLocale()); + registerModule(new AdminPurge()); + registerModule(new AdminReload()); + registerModule(new AdminRemove()); + registerModule(new AdminReport()); + registerModule(new AdminVersion()); + registerModule(new AdminQuery()); + registerModule(new AdminPurgeBanned()); + registerModule(new AdminExpire()); + registerModule(new AdminDump()); + registerModule(new AdminRebuild()); + registerModule(new AdminBackup()); + registerModule(new AdminView()); + + // /lwc setup + registerModule(new BaseSetupModule()); + registerModule(new DatabaseSetupModule()); + registerModule(new LimitsSetup()); + + // flags + registerModule(new BaseFlagModule()); + registerModule(new RedstoneModule()); + registerModule(new MagnetModule()); + + // modes + registerModule(new BaseModeModule()); + registerModule(new PersistModule()); + registerModule(new DropTransferModule()); + registerModule(new NoSpamModule()); + + // non-core modules but are included with LWC anyway + if (resolvePlugin("WorldGuard") != null) { + registerModule(new WorldGuard()); + } + + if (resolvePlugin("Towny") != null) { + registerModule(new Towny()); + } + } + + /** + * Register a module + * + * @param module + */ + private void registerModule(Module module) { + moduleLoader.registerModule(plugin, module); + } + + /** + * Get a plugin by the name. Does not have to be enabled, and will remain disabled if it is disabled. + * + * @param name + * @return + */ + private Plugin resolvePlugin(String name) { + Plugin temp = plugin.getServer().getPluginManager().getPlugin(name); + + if (temp == null) { + return null; + } + + return temp; + } + + /** + * Merge inventories into one + * + * @param blocks + * @return + */ + public ItemStack[] mergeInventories(List blocks) { + ItemStack[] stacks = new ItemStack[54]; + int index = 0; + + try { + for (Block block : blocks) { + if (!(block.getState() instanceof InventoryHolder)) { + continue; + } + + InventoryHolder holder = (InventoryHolder) block.getState(); + Inventory inventory = holder.getInventory(); + + // Add all the items from this inventory + for (ItemStack stack : inventory.getContents()) { + stacks[index] = stack; + index++; + } + } + } catch (Exception e) { + return mergeInventories(blocks); + } + + return stacks; + } + + /** + * Process rights inputted for a protection and add or remove them to the given protection + * + * @param sender + * @param protection + * @param arguments + */ + public void processRightsModifications(CommandSender sender, Protection protection, String... arguments) { + // Does it match a protection type? + try { + Protection.Type protectionType = Protection.Type.matchType(arguments[0]); + + if (protectionType != null) { + protection.setType(protectionType); + protection.save(); + + // If it's being passworded, we need to set the password + if (protectionType == Protection.Type.PASSWORD) { + String password = StringUtil.join(arguments, 1); + protection.setPassword(encrypt(password)); + } + + sendLocale(sender, "protection.typechanged", "type", plugin.getMessageParser().parseMessage(protectionType.toString().toLowerCase())); + return; + } + } catch (IllegalArgumentException e) { + // It's normal for this to be thrown if nothing was matched + } + + for (String value : arguments) { + boolean remove = false; + boolean isAdmin = false; + Permission.Type type = Permission.Type.PLAYER; + + // Gracefully ignore id + if (value.startsWith("id:")) { + continue; + } + + if (value.startsWith("-")) { + remove = true; + value = value.substring(1); + } + + if (value.startsWith("@")) { + isAdmin = true; + value = value.substring(1); + } + + if (value.toLowerCase().startsWith("p:")) { + type = Permission.Type.PLAYER; + value = value.substring(2); + } + + if (value.toLowerCase().startsWith("g:")) { + type = Permission.Type.GROUP; + value = value.substring(2); + } + + if (value.toLowerCase().startsWith("t:")) { + type = Permission.Type.TOWN; + value = value.substring(2); + } + + if (value.toLowerCase().startsWith("town:")) { + type = Permission.Type.TOWN; + value = value.substring(5); + } + + if (value.toLowerCase().startsWith("item:")) { + type = Permission.Type.ITEM; + value = value.substring(5); + } + + if (value.toLowerCase().startsWith("r:")) { + type = Permission.Type.REGION; + value = value.substring(2); + } + + if (value.toLowerCase().startsWith("region:")) { + type = Permission.Type.REGION; + value = value.substring(7); + } + + if (value.trim().isEmpty()) { + continue; + } + + String localeChild = type.toString().toLowerCase(); + + // If it's a player, convert it to UUID + if (type == Permission.Type.PLAYER) { + UUID uuid = UUIDRegistry.getUUID(value); + + if (uuid != null) { + value = uuid.toString(); + } + } + + if (!remove) { + Permission permission = new Permission(value, type); + permission.setAccess(isAdmin ? Permission.Access.ADMIN : Permission.Access.PLAYER); + + // add it to the protection and queue it to be saved + protection.addPermission(permission); + protection.save(); + + if (type == Permission.Type.PLAYER) { + sendLocale(sender, "protection.interact.rights.register." + localeChild, "name", UUIDRegistry.formatPlayerName(value), "isadmin", isAdmin ? "[" + Colors.Red + "ADMIN" + Colors.Gold + "]" : ""); + } else { + sendLocale(sender, "protection.interact.rights.register." + localeChild, "name", value, "isadmin", isAdmin ? "[" + Colors.Red + "ADMIN" + Colors.Gold + "]" : ""); + } + } else { + protection.removePermissions(value, type); + protection.save(); + + if (type == Permission.Type.PLAYER) { + sendLocale(sender, "protection.interact.rights.remove." + localeChild, "name", UUIDRegistry.formatPlayerName(value), "isadmin", isAdmin ? "[" + Colors.Red + "ADMIN" + Colors.Gold + "]" : ""); + } else { + sendLocale(sender, "protection.interact.rights.remove." + localeChild, "name", value, "isadmin", isAdmin ? "[" + Colors.Red + "ADMIN" + Colors.Gold + "]" : ""); + } + } + } + } + + /** + * Reload internal data structures + */ + public void reload() { + plugin.loadLocales(); + protectionConfigurationCache.clear(); + Configuration.reload(); + moduleLoader.dispatchEvent(new LWCReloadEvent()); + } + + /** + * Reload the database + */ + public void reloadDatabase() { + try { + databaseThread.flush(); + databaseThread.stop(); + physicalDatabase = new PhysDB(); + physicalDatabase.connect(); + physicalDatabase.load(); + databaseThread = new DatabaseThread(this); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Remove all modes if the player is not in persistent mode + * + * @param sender + */ + public void removeModes(CommandSender sender) { + if (sender instanceof Player) { + Player bPlayer = (Player) sender; + + if (notInPersistentMode(bPlayer.getName())) { + wrapPlayer(bPlayer).removeAllActions(); + } + } else if (sender instanceof LWCPlayer) { + removeModes(((LWCPlayer) sender).getBukkitPlayer()); + } + } + + /** + * Return if the player is in persistent mode + * + * @param player the player to check + * @return true if the player is NOT in persistent mode + */ + public boolean notInPersistentMode(String player) { + return !wrapPlayer(Bukkit.getServer().getPlayer(player)).hasMode("persist"); + } + + /** + * Send the full help to a player + * + * @param sender the player to send to + */ + public void sendFullHelp(CommandSender sender) { + sendLocale(sender, "help.basic"); + + if (isAdmin(sender)) { + sender.sendMessage(""); + sender.sendMessage(Colors.Red + "/lwc admin - Administration"); + } + } + + /** + * Send the simple usage of a command + * + * @param player + * @param command + */ + public void sendSimpleUsage(CommandSender player, String command) { + sendLocale(player, "help.simpleusage", "command", command); + } + + /** + * @return the configuration object + */ + public Configuration getConfiguration() { + return configuration; + } + + /** + * @return the Currency handler + */ + public ICurrency getCurrency() { + return currency; + } + + /** + * @return the backup manager + */ + public BackupManager getBackupManager() { + return backupManager; + } + + /** + * @return the module loader + */ + public ModuleLoader getModuleLoader() { + return moduleLoader; + } + + /** + * @return the Permissions handler + */ + public IPermissions getPermissions() { + return permissions; + } + + /** + * @return physical database object + */ + public PhysDB getPhysicalDatabase() { + return physicalDatabase; + } + + /** + * @return the plugin class + */ + public LWCPlugin getPlugin() { + return plugin; + } + + /** + * @return the protection cache + */ + public ProtectionCache getProtectionCache() { + return protectionCache; + } + + /** + * @return the update thread + */ + public DatabaseThread getDatabaseThread() { + return databaseThread; + } + + /** + * @return the plugin version + */ + public double getVersion() { + return Double.parseDouble(plugin.getDescription().getVersion()); + } + + /** + * @return true if history logging is enabled + */ + public boolean isHistoryEnabled() { + return !configuration.getBoolean("core.disableHistory", false); + } + +} diff --git a/core/src/main/java/com/griefcraft/lwc/LWCInfo.java b/core/src/main/java/com/griefcraft/lwc/LWCInfo.java new file mode 100644 index 000000000..ffb0fb30e --- /dev/null +++ b/core/src/main/java/com/griefcraft/lwc/LWCInfo.java @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.lwc; + +import com.griefcraft.util.Version; + +public class LWCInfo { + + /** + * Full LWC version + */ + public static Version FULL_VERSION; + + /** + * Sets LWC's version + * + * @param version + */ + public static void setVersion(String version) { + String implementationVersion = LWCPlugin.class.getPackage().getImplementationVersion(); + FULL_VERSION = new Version(version + " " + implementationVersion); + } + +} diff --git a/core/src/main/java/com/griefcraft/lwc/LWCPlugin.java b/core/src/main/java/com/griefcraft/lwc/LWCPlugin.java new file mode 100644 index 000000000..7d13af93a --- /dev/null +++ b/core/src/main/java/com/griefcraft/lwc/LWCPlugin.java @@ -0,0 +1,349 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.lwc; + +import com.griefcraft.listeners.LWCBlockListener; +import com.griefcraft.listeners.LWCEntityListener; +import com.griefcraft.listeners.LWCPlayerListener; +import com.griefcraft.listeners.LWCServerListener; +import com.griefcraft.scripting.event.LWCCommandEvent; +import com.griefcraft.sql.Database; +import com.griefcraft.util.StringUtil; +import com.griefcraft.util.Updater; +import com.griefcraft.util.locale.LWCResourceBundle; +import com.griefcraft.util.locale.LocaleClassLoader; +import com.griefcraft.util.locale.UTF8Control; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.jar.JarFile; + +public class LWCPlugin extends JavaPlugin { + + /** + * The LWC instance + */ + private LWC lwc; + + /** + * The message parser to parse messages with + */ + private MessageParser messageParser; + + /** + * LWC updater + */ + private Updater updater; + + @Override + public boolean onCommand(CommandSender sender, Command command, String commandLabel, String[] args) { + String commandName = command.getName().toLowerCase(); + String argString = StringUtil.join(args, 0); + boolean isPlayer = (sender instanceof Player); // check if they're a player + + // these can only apply to players, not the console (who has absolute player :P) + if (isPlayer) { + // Aliases + String aliasCommand = null; + String[] aliasArgs = new String[0]; + + if (commandName.equals("cpublic")) { + aliasCommand = "create"; + aliasArgs = new String[]{"public"}; + } else if (commandName.equals("cpassword")) { + aliasCommand = "create"; + aliasArgs = ("password " + argString).split(" "); + } else if (commandName.equals("cprivate") || commandName.equals("lock")) { + aliasCommand = "create"; + aliasArgs = ("private " + argString).split(" "); + } else if (commandName.equals("cdonation")) { + aliasCommand = "create"; + aliasArgs = ("donation " + argString).split(" "); + } else if (commandName.equals("cmodify")) { + aliasCommand = "modify"; + aliasArgs = argString.isEmpty() ? new String[0] : argString.split(" "); + } else if (commandName.equals("cinfo")) { + aliasCommand = "info"; + } else if (commandName.equals("cunlock")) { + aliasCommand = "unlock"; + aliasArgs = argString.isEmpty() ? new String[0] : argString.split(" "); + } else if (commandName.equals("cremove") || commandName.equals("unlock")) { + aliasCommand = "remove"; + aliasArgs = new String[]{"protection"}; + } else if (commandName.equals("climits")) { + aliasCommand = "limits"; + aliasArgs = argString.isEmpty() ? new String[0] : argString.split(" "); + } else if (commandName.equals("cadmin")) { + aliasCommand = "admin"; + aliasArgs = argString.isEmpty() ? new String[0] : argString.split(" "); + } else if (commandName.equals("cremoveall")) { + aliasCommand = "remove"; + aliasArgs = new String[]{"allprotections"}; + } + + // Flag aliases + if (commandName.equals("credstone")) { + aliasCommand = "flag"; + aliasArgs = ("redstone " + argString).split(" "); + } else if (commandName.equals("cmagnet")) { + aliasCommand = "flag"; + aliasArgs = ("magnet " + argString).split(" "); + } else if (commandName.equals("cexempt")) { + aliasCommand = "flag"; + aliasArgs = ("exemption " + argString).split(" "); + } else if (commandName.equals("cautoclose")) { + aliasCommand = "flag"; + aliasArgs = ("autoclose " + argString).split(" "); + } else if (commandName.equals("callowexplosions") || commandName.equals("ctnt")) { + aliasCommand = "flag"; + aliasArgs = ("allowexplosions " + argString).split(" "); + } else if (commandName.equals("chopper")) { + aliasCommand = "flag"; + aliasArgs = ("hopper " + argString).split(" "); + } + + // Mode aliases + if (commandName.equals("cdroptransfer")) { + aliasCommand = "mode"; + aliasArgs = ("droptransfer " + argString).split(" "); + } else if (commandName.equals("cpersist")) { + aliasCommand = "mode"; + aliasArgs = ("persist " + argString).split(" "); + } else if (commandName.equals("cnospam")) { + aliasCommand = "mode"; + aliasArgs = ("nospam " + argString).split(" "); + } + + if (aliasCommand != null) { + lwc.getModuleLoader().dispatchEvent(new LWCCommandEvent(sender, aliasCommand, aliasArgs)); + return true; + } + } + + if (args.length == 0) { + lwc.sendFullHelp(sender); + return true; + } + + ///// Dispatch command to modules + LWCCommandEvent evt = new LWCCommandEvent(sender, args[0].toLowerCase(), args.length > 1 ? StringUtil.join(args, 1).split(" ") : new String[0]); + lwc.getModuleLoader().dispatchEvent(evt); + + if (evt.isCancelled()) { + return true; + } + + if (!isPlayer) { + lwc.sendLocale(sender, "lwc.commandnotsupported"); + return true; + } + + return false; + } + + public void onDisable() { + LWC.ENABLED = false; + + if (lwc != null) { + lwc.destruct(); + } + + // cancel all tasks we created + getServer().getScheduler().cancelTasks(this); + } + + public void onEnable() { + preload(); + lwc = new LWC(this); + + LWCInfo.setVersion(getDescription().getVersion()); + LWC.ENABLED = true; + + loadLocales(); + loadDatabase(); + + // Load the rest of LWC + lwc.load(); + + try { + registerEvents(); + } catch (NoSuchFieldError e) { + } + } + + /** + * Load the database + */ + public void loadDatabase() { + String database = lwc.getConfiguration().getString("database.adapter"); + + if (database.equalsIgnoreCase("mysql")) { + Database.DefaultType = Database.Type.MySQL; + } else { + Database.DefaultType = Database.Type.SQLite; + } + } + + /** + * Load LWC localizations + */ + public void loadLocales() { + LWCResourceBundle locale; + String localization = getCurrentLocale(); + + // located in plugins/LWC/locale/, values in that overrides the ones in the default :-) + ResourceBundle optionalBundle = null; + + try { + ResourceBundle defaultBundle; + + // Open the LWC jar file + JarFile file = new JarFile(getFile()); + + // Attempt to load the default locale + defaultBundle = new PropertyResourceBundle(new InputStreamReader(file.getInputStream(file.getJarEntry("lang/lwc_en.properties")), "UTF-8")); + locale = new LWCResourceBundle(defaultBundle); + + try { + optionalBundle = ResourceBundle.getBundle("lwc", new Locale(localization), new LocaleClassLoader(), new UTF8Control()); + } catch (MissingResourceException e) { + } + + if (optionalBundle != null) { + locale.addExtensionBundle(optionalBundle); + } + + // and now check if a bundled locale the same as the server's locale exists + try { + optionalBundle = new PropertyResourceBundle(new InputStreamReader(file.getInputStream(file.getJarEntry("lang/lwc_" + localization + ".properties")), "UTF-8")); + } catch (MissingResourceException e) { + } catch (NullPointerException e) { + // file wasn't found :p - that's ok + } + + // ensure both bundles aren't the same + if (defaultBundle == optionalBundle) { + optionalBundle = null; + } + + if (optionalBundle != null) { + locale.addExtensionBundle(optionalBundle); + } + } catch (MissingResourceException e) { + log("We are missing the default locale in LWC.jar.. What happened to it? :-("); + throw e; + } catch (IOException e) { + log("Uh-oh: " + e.getMessage()); + return; + } + + // create the message parser + messageParser = new SimpleMessageParser(locale); + } + + /** + * Load shared libraries and other misc things + */ + private void preload() { + updater = new Updater(); + + // Set the SQLite native library path + String nativeLibraryFolder = updater.getOSSpecificFolder(); + + if (nativeLibraryFolder != null) { + System.setProperty("org.sqlite.lib.path", nativeLibraryFolder); + } + } + + /** + * Log a string to the console + * + * @param str + */ + private void log(String str) { + getLogger().info(str); + } + + /** + * Register all of the events used by LWC + */ + private void registerEvents() { + PluginManager pluginManager = Bukkit.getServer().getPluginManager(); + pluginManager.registerEvents(new LWCPlayerListener(this), this); + pluginManager.registerEvents(new LWCEntityListener(this), this); + pluginManager.registerEvents(new LWCBlockListener(this), this); + pluginManager.registerEvents(new LWCServerListener(this), this); + } + + /** + * @return the current locale in use + */ + public String getCurrentLocale() { + return lwc.getConfiguration().getString("core.locale", "en"); + } + + /** + * @return the LWC instance + */ + public LWC getLWC() { + return lwc; + } + + /** + * Gets the message parser + * @return + */ + public MessageParser getMessageParser() { + return messageParser; + } + + /** + * @return the Updater instance + */ + public Updater getUpdater() { + return updater; + } + + @Override + public File getFile() { + return super.getFile(); + } + +} diff --git a/core/src/main/java/com/griefcraft/lwc/MessageParser.java b/core/src/main/java/com/griefcraft/lwc/MessageParser.java new file mode 100644 index 000000000..e064b3f54 --- /dev/null +++ b/core/src/main/java/com/griefcraft/lwc/MessageParser.java @@ -0,0 +1,47 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.lwc; + +/** + * Parses i18n coded strings and also allows dynamic binds such as %msg% where msg is a variable given + */ +public interface MessageParser { + + /** + * Retrieve and parse a localized key using the arguments. The arguments are parsed in the format of + * "key1" value1 "key2" value2 ... + * + * @param key + * @param args + * @return + */ + public String parseMessage(String key, Object... args); + + +} diff --git a/core/src/main/java/com/griefcraft/lwc/SimpleMessageParser.java b/core/src/main/java/com/griefcraft/lwc/SimpleMessageParser.java new file mode 100644 index 000000000..b24ae0403 --- /dev/null +++ b/core/src/main/java/com/griefcraft/lwc/SimpleMessageParser.java @@ -0,0 +1,158 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.lwc; + +import com.griefcraft.util.Colors; +import com.griefcraft.util.StringUtil; + +import java.util.HashMap; +import java.util.Map; +import java.util.ResourceBundle; + +public class SimpleMessageParser implements MessageParser { + + /** + * The i18n localization bundle + */ + private final ResourceBundle locale; + + /** + * Cached messages + */ + private final Map basicMessageCache = new HashMap(); + + /** + * A heavy cache that includes binds. + */ + private final Map bindMessageCache = new HashMap(); + + public SimpleMessageParser(ResourceBundle locale) { + this.locale = locale; + } + + public String parseMessage(String key, Object... args) { + key = StringUtil.fastReplace(key, ' ', '_'); + + // For the bind cache + String cacheKey = key; + + // add the arguments to the cache key + if (args != null && args.length > 0) { + for (Object argument : args) { + cacheKey += argument.toString(); + } + } + + if (bindMessageCache.containsKey(cacheKey)) { + return bindMessageCache.get(cacheKey); + } + + if (!locale.containsKey(key)) { + return null; + } + + Map bind = parseBinds(args); + String value = basicMessageCache.get(key); + + if (value == null) { + value = locale.getString(key); + + // apply colors + for (String colorKey : Colors.localeColors.keySet()) { + String color = Colors.localeColors.get(colorKey); + + if (value.contains(colorKey)) { + value = StringUtil.fastReplace(value, colorKey, color); + } + } + + // Apply aliases + String[] aliasvars = new String[]{"cprivate", "cpublic", "cpassword", "cmodify", "cunlock", "cinfo", "cremove"}; + + // apply command name modification depending on menu style + for (String alias : aliasvars) { + String replace = "%" + alias + "%"; + + if (!value.contains(replace)) { + continue; + } + + String localeName = alias + ".basic"; + value = value.replace(replace, parseMessage(localeName)); + } + + // Cache it + basicMessageCache.put(key, value); + } + + // apply binds + for (String bindKey : bind.keySet()) { + Object object = bind.get(bindKey); + + value = StringUtil.fastReplace(value, "%" + bindKey + "%", object.toString()); + } + + // include the binds + bindMessageCache.put(cacheKey, value); + return value; + } + + /** + * Convert an even-lengthed argument array to a map containing String keys i.e parseBinds("Test", null, "Test2", obj) = Map().put("test", null).put("test2", obj) + * + * @param args + * @return + */ + private Map parseBinds(Object... args) { + Map bind = new HashMap(); + + if (args == null || args.length < 2) { + return bind; + } + + if (args.length % 2 != 0) { + throw new IllegalArgumentException("The given arguments length must be equal"); + } + + int size = args.length; + for (int index = 0; index < args.length; index += 2) { + if ((index + 2) > size) { + break; + } + + String key = args[index].toString(); + Object object = args[index + 1]; + + bind.put(key, object); + } + + return bind; + } + +} diff --git a/core/src/main/java/com/griefcraft/migration/ConfigPost300.java b/core/src/main/java/com/griefcraft/migration/ConfigPost300.java new file mode 100644 index 000000000..f489de9e2 --- /dev/null +++ b/core/src/main/java/com/griefcraft/migration/ConfigPost300.java @@ -0,0 +1,170 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.migration; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.modules.pluginsupport.WorldGuard; +import org.bukkit.Material; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Logger; + +public class ConfigPost300 implements MigrationUtility { + private static Logger logger = Logger.getLogger("Patcher"); + + // contains the core config equivalent key pairs + // e.g locale->core.locale + private static Map mappings = null; + + public void run() { + LWC lwc = LWC.getInstance(); + File configFile = new File("plugins/LWC/lwc.properties"); + + if (!configFile.exists()) { + return; + } + + // delete internal.ini + new File("plugins/LWC/internal.ini").delete(); + logger.info("Converting lwc.properties to new variants"); + + // we need to convert.. + populate(); + + // load lwc.properties + Properties old = new Properties(); + + try { + InputStream inputStream = new FileInputStream(configFile); + old.load(inputStream); + inputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + return; + } + + // convert the easy to do mappings + for (Map.Entry entry : mappings.entrySet()) { + String oldKey = entry.getKey(); + String newKey = entry.getValue(); + + // Don't mind me, just making the converted values appear correctly! + try { + lwc.getConfiguration().setProperty(newKey, Integer.parseInt(old.getProperty(oldKey, ""))); + } catch (Exception e) { + String value = old.getProperty(oldKey, ""); + + if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + lwc.getConfiguration().setProperty(newKey, Boolean.parseBoolean(old.getProperty(oldKey, ""))); + } else { + lwc.getConfiguration().setProperty(newKey, old.getProperty(oldKey, "")); + } + } + + } + + // custom mappings, can't be easily done + + // protection blacklist + String protectionBlacklist = old.getProperty("protection-blacklist", "").trim(); + + if (!protectionBlacklist.isEmpty()) { + String[] split = protectionBlacklist.replaceAll(" ", "_").split(","); + + for (String protection : split) { + int blockId = 0; + + try { + blockId = Integer.parseInt(protection); + } catch (NumberFormatException e) { + } + + // if it's an int, convert it + if (blockId > 0) { + protection = Material.getMaterial(blockId).toString().toLowerCase().replaceAll("block", ""); + + if (protection.endsWith("_")) { + protection = protection.substring(0, protection.length() - 1); + } + } + + lwc.getConfiguration().setProperty("protections.blocks." + protection + ".enabled", false); + } + } + + // WorldGuard + String enforceWorldGuard = old.getProperty("enforce-worldguard-regions"); + + if (Boolean.parseBoolean(enforceWorldGuard)) { + WorldGuard worldGuard = (WorldGuard) lwc.getModuleLoader().getModule(WorldGuard.class); + List regions = null; + + String oldRegions = old.getProperty("worldguard-allowed-regions"); + regions = Arrays.asList(oldRegions.split(",")); + + worldGuard.set("worldguard.enabled", true); + worldGuard.set("worldguard.regions", regions); + worldGuard.save(); + } + + // we're done, free up the mappings & save + mappings = null; + lwc.getConfiguration().save(); + configFile.delete(); + } + + // populate the mappings table with well the mappings + private static void populate() { + mappings = new HashMap(); + + mappings.put("allow-block-destruction", "protections.allowBlockDestruction"); + mappings.put("auto-update", "core.autoUpdate"); + mappings.put("database", "database.adapter"); + mappings.put("db-path", "database.path"); + mappings.put("default-menu-style", "core.defaultMenuStyle"); + mappings.put("deny-redstone", "protections.denyRedstone"); + mappings.put("flush-db-interval", "core.flushInterval"); + mappings.put("locale", "core.locale"); + mappings.put("mysql-database", "database.database"); + mappings.put("mysql-host", "database.host"); + mappings.put("mysql-pass", "database.password"); + mappings.put("mysql-user", "database.username"); + mappings.put("show-protection-notices", "core.showNotices"); + mappings.put("verbose", "core.verbose"); + mappings.put("op-is-lwcadmin", "core.opIsLWCAdmin"); + } + +} diff --git a/core/src/main/java/com/griefcraft/migration/DatabaseMigrator.java b/core/src/main/java/com/griefcraft/migration/DatabaseMigrator.java new file mode 100644 index 000000000..4bc9b50e9 --- /dev/null +++ b/core/src/main/java/com/griefcraft/migration/DatabaseMigrator.java @@ -0,0 +1,94 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.migration; + +import com.griefcraft.model.History; +import com.griefcraft.model.Protection; +import com.griefcraft.sql.PhysDB; + +import java.util.List; +import java.util.logging.Logger; + +public class DatabaseMigrator { + private static Logger logger = Logger.getLogger("LWCMigrator"); + + /** + * Converts the current database to the given database type + * + * @param fromDatabase The database to convert from + * @param toDatabase The database to convert to - does not need to be initialized; new PhysDB(type) is fine + * @return true if the conversion was most likely successful + */ + public boolean migrate(PhysDB fromDatabase, PhysDB toDatabase) { + try { + toDatabase.getConnection().setAutoCommit(false); + + // some prelim data + int startProtections = toDatabase.getProtectionCount(); + int protectionCount = fromDatabase.getProtectionCount(); + int historyCount = fromDatabase.getHistoryCount(); + int expectedProtections = protectionCount + startProtections; + + if (protectionCount > 0) { + List tmp = fromDatabase.loadProtections(); + + for (Protection protection : tmp) { + // sync it to the live database + protection.saveNow(); + } + + toDatabase.getConnection().commit(); + if (expectedProtections != (protectionCount = fromDatabase.getProtectionCount())) { + logger.info("Weird, only " + protectionCount + " protections are in the database? Continuing..."); + } + } + + if (historyCount > 0) { + List tmp = fromDatabase.loadHistory(); + + for (History history : tmp) { + // make sure it's assumed it does not exist in the database + history.setExists(false); + + // sync the history object with the active database (ala MySQL) + history.sync(); + } + } + + fromDatabase.getConnection().close(); + toDatabase.getConnection().setAutoCommit(true); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return true; + } + +} diff --git a/core/src/main/java/com/griefcraft/migration/MigrationUtility.java b/core/src/main/java/com/griefcraft/migration/MigrationUtility.java new file mode 100644 index 000000000..67513e675 --- /dev/null +++ b/core/src/main/java/com/griefcraft/migration/MigrationUtility.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.migration; + +public interface MigrationUtility { + + /** + * Check and run the utility + */ + public void run(); + +} diff --git a/core/src/main/java/com/griefcraft/migration/MySQLPost200.java b/core/src/main/java/com/griefcraft/migration/MySQLPost200.java new file mode 100644 index 000000000..bf8e4d4c4 --- /dev/null +++ b/core/src/main/java/com/griefcraft/migration/MySQLPost200.java @@ -0,0 +1,104 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.migration; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.sql.Database.Type; +import com.griefcraft.sql.PhysDB; + +import java.io.File; +import java.util.logging.Logger; + +// Sort of just a convenience class, so as to not make the LWC class more cluttered than it is right now +public class MySQLPost200 implements MigrationUtility { + + private static Logger logger = Logger.getLogger("Patcher"); + + /** + * Check for required SQLite->MySQL conversion + */ + public void run() { + LWC lwc = LWC.getInstance(); + PhysDB physicalDatabase = lwc.getPhysicalDatabase(); + + // this patcher only does something exciting if you have mysql enabled + // :-) + if (physicalDatabase.getType() != Type.MySQL) { + return; + } + + // this patcher only does something exciting if the old SQLite database + // still exists :-) + String database = lwc.getConfiguration().getString("database.path"); + + if (database == null || database.trim().equals("")) { + database = "plugins/LWC/lwc.db"; + } + + File file = new File(database); + if (!file.exists()) { + return; + } + + logger.info("######################################################"); + logger.info("######################################################"); + logger.info("SQLite to MySQL conversion required"); + + // rev up those sqlite databases because I sure am hungry for some data... + DatabaseMigrator migrator = new DatabaseMigrator(); + lwc.reloadDatabase(); + + // Load the sqlite database + PhysDB sqlite = new PhysDB(Type.SQLite); + + try { + sqlite.connect(); + sqlite.load(); + } catch (Exception e) { + e.printStackTrace(); + } + + if (migrator.migrate(sqlite, lwc.getPhysicalDatabase())) { + logger.info("Successfully converted."); + logger.info("Renaming \"" + database + "\" to \"" + database + ".old\""); + if (!file.renameTo(new File(database + ".old"))) { + logger.info("NOTICE: FAILED TO RENAME lwc.db!! Please rename this manually!"); + } + + logger.info("SQLite to MySQL conversion is now complete!\n"); + logger.info("Thank you!"); + } else { + logger.info("#### SEVERE ERROR: Something bad happened when converting the database (Oops!)"); + } + + logger.info("######################################################"); + logger.info("######################################################"); + } + +} diff --git a/core/src/main/java/com/griefcraft/model/Action.java b/core/src/main/java/com/griefcraft/model/Action.java new file mode 100644 index 000000000..52e60e056 --- /dev/null +++ b/core/src/main/java/com/griefcraft/model/Action.java @@ -0,0 +1,94 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.model; + +public class Action { + + private String name; + private Protection protection; + private String data; + private LWCPlayer player; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return the Protection associated with this action + */ + public Protection getProtection() { + return protection; + } + + /** + * @return the data + */ + public String getData() { + return data; + } + + /** + * @return the player + */ + public LWCPlayer getPlayer() { + return player; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @param protection the Protection to set + */ + public void setProtection(Protection protection) { + this.protection = protection; + } + + /** + * @param data the data to set + */ + public void setData(String data) { + this.data = data; + } + + /** + * @param player the player to set + */ + public void setPlayer(LWCPlayer player) { + this.player = player; + } + +} diff --git a/core/src/main/java/com/griefcraft/model/ConfirmAction.java b/core/src/main/java/com/griefcraft/model/ConfirmAction.java new file mode 100644 index 000000000..416febb34 --- /dev/null +++ b/core/src/main/java/com/griefcraft/model/ConfirmAction.java @@ -0,0 +1,51 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.model; + +public class ConfirmAction extends Action { + + /** + * The callback + */ + private final Runnable callback; + + public ConfirmAction(Runnable callback) { + this.callback = callback; + setName("confirm"); + } + + /** + * Called when the action has been confirmed via /lwc confirm + */ + public void onConfirm() { + // run the callback + callback.run(); + } + +} diff --git a/core/src/main/java/com/griefcraft/model/Flag.java b/core/src/main/java/com/griefcraft/model/Flag.java new file mode 100644 index 000000000..f4702bb2d --- /dev/null +++ b/core/src/main/java/com/griefcraft/model/Flag.java @@ -0,0 +1,171 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.model; + +import com.griefcraft.util.StringUtil; +import org.json.simple.JSONObject; + +public class Flag { + + /** + * The ordering of this enum MUST NOT change. The ordinal value is stored internally. + * However, the name of a flag may freely change at any time. + */ + public enum Type { + + /** + * Redstone use will be disabled on the protection if protections.denyRedstone = false; + * however if denyRedstone = true, this flag will instead enable redstone on the protection! + */ + REDSTONE, + + /** + * Attracts dropped items within a certain radius into the protection's inventory + */ + MAGNET, + + /** + * Protection is exempt from being auto removed from LWC - e.g /lwc admin expire -remove 2 weeks + */ + EXEMPTION(true), + + /** + * The door will automatically close after the time configured in plugins/LWC/doors.yml + */ + AUTOCLOSE, + + /** + * Allows explosions to blow a protection up + */ + ALLOWEXPLOSIONS, + + /** + * Controls whether or not hoppers can be used on a protection + */ + HOPPER; + + Type() { + this(false); + } + + Type(boolean restricted) { + this.restricted = restricted; + } + + /** + * If the flag is restricted to only LWC admins + */ + private boolean restricted; + + /** + * @return true if the flag should only be usable by LWC admins + */ + public boolean isRestricted() { + return restricted; + } + + } + + /** + * The flag type + */ + private Type type; + + /** + * Flag data + */ + private final JSONObject data = new JSONObject(); + + public Flag(Type type) { + this.type = type; + data.put("id", type.ordinal()); + } + + /** + * Decode JSON data for a flag + * + * @param node + * @return + */ + public static Flag decodeJSON(JSONObject node) { + if (node == null) { + return null; + } + + // decode the type + int ordinal = -1; + + try { + ordinal = Integer.parseInt(node.get("id").toString()); + } catch (NumberFormatException e) { + return null; + } + + // Still not valid.. + if (ordinal == -1) { + return null; + } + + // let's do a range check + Type[] values = Type.values(); + + if (ordinal > values.length) { + return null; + } + + // good good + Type type = values[ordinal]; + + // create the Flag and hand over the data we have + Flag flag = new Flag(type); + flag.getData().putAll(node); + + return flag; + } + + @Override + public String toString() { + return StringUtil.capitalizeFirstLetter(type.toString()); + } + + /** + * @return + */ + public Type getType() { + return type; + } + + /** + * @return + */ + public JSONObject getData() { + return data; + } + +} diff --git a/core/src/main/java/com/griefcraft/model/History.java b/core/src/main/java/com/griefcraft/model/History.java new file mode 100644 index 000000000..e9c1079d0 --- /dev/null +++ b/core/src/main/java/com/griefcraft/model/History.java @@ -0,0 +1,479 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.model; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.util.StringUtil; + +import java.util.Arrays; +import java.util.List; + +public class History { + + /** + * The history type defines what this History object is for, such as TRANSACTION. + *

+ * Ordering must remain constant as internally, the ordinal is used and + * if ordering is changed, LWC will experience undefined behaviour. + */ + public enum Type { + + /** + * Designates that this history object is for a transaction - e.g when a protection is removed + */ + TRANSACTION + + } + + /** + * The status of this History object; most often ACTIVE or INACTIVE. + *

+ * As with {@link Type}, the ordering must remain constant and not change. + */ + public enum Status { + + /** + * This type is still active + */ + ACTIVE, + + /** + * For some reason the type is now inactive. Most likely the + * protection was removed by the player + */ + INACTIVE + } + + /** + * History id in the database + */ + private int id; + + /** + * Affected protection id + */ + private int protectionId; + + /** + * The protection known for this history object + */ + private Protection protection; + + /** + * The player that caused the history action to be created + */ + private String player; + + /** + * The x coordinate of the history item + */ + private int x; + + /** + * The y coordinate of the history item + */ + private int y; + + /** + * The z coordinate of the history item + */ + private int z; + + /** + * The history type, e.g TRANSACTION + */ + private Type type = Type.TRANSACTION; + + /** + * The status (ACTIVE or INACTIVE normally) + */ + private Status status = Status.INACTIVE; + + /** + * Metadata about the transaction. An example of one entry would be + * for iConomy prices to be pushed in here. Any module can modify the + * meta data and add their own data about the transaction. + */ + private String[] metadata; + + /** + * The seconds (since linux epoch) this History object was created + */ + private long timestamp; + + /** + * If the history val exists in the database + */ + private boolean exists = false; + + /** + * If the history object was modified + */ + private boolean modified = false; + + /** + * If the History object is waiting to be flushed to the database + */ + private boolean saving = false; + + public History() { + // set some defaults to account for stupidness + status = Status.INACTIVE; + metadata = new String[0]; + } + + /** + * @return true if the history object should be synced to the database + */ + public boolean wasModified() { + return modified; + } + + /** + * @return the Protection this history value is associated with + */ + public Protection getProtection() { + // attempt to load the protection if it hasn't been loaded yet + if (protection == null) { + this.protection = LWC.getInstance().getPhysicalDatabase().loadProtection(protectionId); + } + + return protection; + } + + /** + * Add a string of data to the stored metadata + * + * @param data + */ + public void addMetaData(String data) { + String[] temp = new String[metadata.length + 1]; + System.arraycopy(metadata, 0, temp, 0, metadata.length); + + // push the data to the end of the temporary array + // array.length doesn't start at 0, so we can be sure this is valid + temp[metadata.length] = data; + + // we're okey + this.metadata = temp; + this.modified = true; + } + + /** + * Check if the metadata contains a given key + * + * @param key + * @return + */ + public boolean hasKey(String key) { + return getMetaDataStartsWith(key + "=") != null; + } + + /** + * Get a boolean value from the metadata using the key (key=value) + * + * @param key + * @return + */ + public boolean getBoolean(String key) { + String metadata = getMetaDataStartsWith(key + "="); + + return metadata != null && Boolean.parseBoolean(metadata.substring((key + "=").length())); + + } + + /** + * Get a String value from the metadata using the key (key=value) + * + * @param key + * @return + */ + public String getString(String key) { + String metadata = getMetaDataStartsWith(key + "="); + + if (metadata == null) { + return ""; + } + + return metadata.substring((key + "=").length()); + } + + /** + * Get an integer value from the metadata using the key (key=value) + * + * @param key + * @return + */ + public int getInteger(String key) { + String metadata = getMetaDataStartsWith(key + "="); + + if (metadata == null) { + return 0; + } + + return Integer.parseInt(metadata.substring((key + "=").length())); + } + + /** + * Get a double value from the metadata using the key (key=value) + * + * @param key + * @return + */ + public double getDouble(String key) { + String metadata = getMetaDataStartsWith(key + "="); + + if (metadata == null) { + return 0; + } + + return Double.parseDouble(metadata.substring((key + "=").length())); + } + + /** + * Get a metadata that starts with a specific string + * + * @param startsWith + * @return the full metadata if a match is found, otherwise NULL + */ + public String getMetaDataStartsWith(String startsWith) { + for (String temp : metadata) { + if (temp.startsWith(startsWith)) { + return temp; + } + } + + return null; + } + + /** + * Remove a string of known data from the stored metadata + * + * @param data + * @return true if the given metadata was successfully removed + */ + public boolean removeMetaData(String data) { + // sorry + List temp = Arrays.asList(metadata); + int expected = metadata.length - 1; + + temp.remove(data); + + // that went better than expected + this.metadata = temp.toArray(new String[temp.size()]); + this.modified = true; + + return metadata.length == expected; + } + + /** + * Set the cached protection this History object belongs to save a query or two later on + * + * @param protection + */ + public void setProtection(Protection protection) { + if (protection == null) { + return; + } + + this.protection = protection; + this.protectionId = protection.getId(); + } + + /** + * @return true if this History object exists in the database + */ + public boolean doesExist() { + return exists; + } + + /** + * Set if the History object exists in the database + * + * @param exists + */ + public void setExists(boolean exists) { + this.exists = exists; + this.modified = true; + } + + /** + * @return a STRING representation of the metadata for use in the database + */ + public String getSafeMetaData() { + return StringUtil.join(metadata, 0, ","); + } + + /** + * Sync this history object to the database when possible + */ + public void save() { + // if it was not modified, no point in saving it :-) + if (!modified || saving) { + return; + } + + LWC lwc = LWC.getInstance(); + + // find the protection the history object is attached to + Protection protection = getProtection(); + + // no protection? weird, just sync anyway + if (protection == null) { + saveNow(); + return; + } + + // wait! + this.saving = true; + + // ensure the protection knows about us + protection.checkHistory(this); + + // save it when possible + protection.save(); + } + + /** + * Force the history object to be saved immediately + */ + public void saveNow() { + LWC.getInstance().getPhysicalDatabase().saveHistory(this); + this.modified = false; + this.saving = false; + } + + /** + * Alias for {@see save} + */ + public void sync() { + save(); + } + + /** + * Remove this history object from the database + * TODO: broadcast an event + */ + public void remove() { + LWC.getInstance().getPhysicalDatabase().removeHistory(id); + this.modified = false; + } + + public int getId() { + return id; + } + + public int getProtectionId() { + return protectionId; + } + + public String getPlayer() { + return player; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public Type getType() { + return type; + } + + public Status getStatus() { + return status; + } + + public String[] getMetaData() { + return metadata; + } + + public long getTimestamp() { + return timestamp; + } + + public void setId(int id) { + this.id = id; + this.exists = true; + this.modified = true; + } + + public void setProtectionId(int protectionId) { + this.protectionId = protectionId; + this.modified = true; + } + + public void setPlayer(String player) { + this.player = player; + this.modified = true; + } + + public void setX(int x) { + this.x = x; + this.modified = true; + } + + public void setY(int y) { + this.y = y; + this.modified = true; + } + + public void setZ(int z) { + this.z = z; + this.modified = true; + } + + public void setType(Type type) { + this.type = type; + this.modified = true; + } + + public void setStatus(Status status) { + this.status = status; + this.modified = true; + } + + public void setMetaData(String[] metadata) { + this.metadata = metadata; + this.modified = true; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + this.modified = true; + } + +} diff --git a/core/src/main/java/com/griefcraft/model/LWCPlayer.java b/core/src/main/java/com/griefcraft/model/LWCPlayer.java new file mode 100644 index 000000000..9aa6f3017 --- /dev/null +++ b/core/src/main/java/com/griefcraft/model/LWCPlayer.java @@ -0,0 +1,420 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.model; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.modules.history.HistoryModule; +import org.bukkit.Server; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class LWCPlayer implements CommandSender { + + /** + * The LWC instance + */ + private LWC lwc; + + /** + * The player instance + */ + private Player player; + + /** + * Cache of LWCPlayer objects + */ + private final static Map playerCache = new HashMap(); + + /** + * The map of actions the player has + */ + private final Map actions = new HashMap(); + + /** + * The set of modes the player has + */ + private final Set modes = new HashSet(); + + /** + * The set of protections the player can access + */ + private final Set accessibleProtections = new HashSet(); + + public LWCPlayer(LWC lwc, Player player) { + this.lwc = lwc; + this.player = player; + } + + /** + * Get the LWCPlayer object from a Player object + * + * @param player + * @return + */ + public static LWCPlayer getPlayer(Player player) { + if (!playerCache.containsKey(player)) { + playerCache.put(player, new LWCPlayer(LWC.getInstance(), player)); + } + + return playerCache.get(player); + } + + /** + * Remove a player from the player cache + * + * @param player + */ + public static void removePlayer(Player player) { + LWCPlayer lwcPlayer = getPlayer(player); + + // uncache them + playerCache.remove(player); + } + + /** + * @return the Bukkit Player object + */ + public Player getBukkitPlayer() { + return player; + } + + /** + * Get the player's UUID + * + * @return player's UUID + */ + public UUID getUniqueId() { + return player.getUniqueId(); + } + + /** + * @return the player's name + */ + public String getName() { + return player.getName(); + } + + /** + * Enable a mode on the player + * + * @param mode + * @return + */ + public boolean enableMode(Mode mode) { + return modes.add(mode); + } + + /** + * Disable a mode on the player + * + * @param mode + * @return + */ + public boolean disableMode(Mode mode) { + return modes.remove(mode); + } + + /** + * Disable all modes enabled by the player + * + * @return + */ + public void disableAllModes() { + modes.clear(); + } + + /** + * Check if the player has an action + * + * @param name + * @return + */ + public boolean hasAction(String name) { + return actions.containsKey(name); + } + + /** + * Get the action represented by the name + * + * @param name + * @return + */ + public Action getAction(String name) { + return actions.get(name); + } + + /** + * Add an action + * + * @param action + * @return + */ + public boolean addAction(Action action) { + actions.put(action.getName(), action); + return true; + } + + /** + * Remove an action + * + * @param action + * @return + */ + public boolean removeAction(Action action) { + actions.remove(action.getName()); + return true; + } + + /** + * Remove all actions + */ + public void removeAllActions() { + actions.clear(); + } + + /** + * Retrieve a Mode object for a player + * + * @param name + * @return + */ + public Mode getMode(String name) { + for (Mode mode : modes) { + if (mode.getName().equals(name)) { + return mode; + } + } + + return null; + } + + /** + * Check if the player has the given mode + * + * @param name + * @return + */ + public boolean hasMode(String name) { + return getMode(name) != null; + } + + /** + * @return the Set of modes the player has activated + */ + public Set getModes() { + return new HashSet(modes); + } + + /** + * @return the Set of actions the player has + */ + public Map getActions() { + return new HashMap(actions); + } + + /** + * @return a Set containing all of the action names + */ + public Set getActionNames() { + return new HashSet(actions.keySet()); + } + + /** + * @return the set of protections the player can temporarily access + */ + public Set getAccessibleProtections() { + return new HashSet(accessibleProtections); + } + + /** + * Add an accessible protection for the player + * + * @param protection + * @return + */ + public boolean addAccessibleProtection(Protection protection) { + return accessibleProtections.add(protection); + } + + /** + * Remove an accessible protection from the player + * + * @param protection + * @return + */ + public boolean removeAccessibleProtection(Protection protection) { + return accessibleProtections.remove(protection); + } + + /** + * Remove all accessible protections + */ + public void removeAllAccessibleProtections() { + accessibleProtections.clear(); + } + + /** + * Create a History object that is attached to this protection + * + * @return + */ + public History createHistoryObject() { + History history = new History(); + + history.setPlayer(player.getName()); + history.setStatus(History.Status.INACTIVE); + + return history; + } + + /** + * Send a locale to the player + * + * @param key + * @param args + */ + public void sendLocale(String key, Object... args) { + lwc.sendLocale(player, key, args); + } + + /** + * Get the player's history + * + * @return + */ + public List getRelatedHistory() { + return lwc.getPhysicalDatabase().loadHistory(player); + } + + /** + * Get the player's history for a given page + * + * @param page + * @return + */ + public List getRelatedHistory(int page) { + return lwc.getPhysicalDatabase().loadHistory(player, (page - 1) * HistoryModule.ITEMS_PER_PAGE, HistoryModule.ITEMS_PER_PAGE); + } + + /** + * Get the player's history pertaining to the type + * + * @param type + * @return + */ + public List getRelatedHistory(History.Type type) { + List related = new ArrayList(); + + for (History history : getRelatedHistory()) { + if (history.getType() == type) { + related.add(history); + } + } + + return related; + } + + public void sendMessage(String s) { + player.sendMessage(s); + } + + public void sendMessage(String[] s) { + for (String _s : s) { + sendMessage(_s); + } + } + + public Server getServer() { + return player.getServer(); + } + + public boolean isPermissionSet(String s) { + return player.isPermissionSet(s); + } + + public boolean isPermissionSet(Permission permission) { + return player.isPermissionSet(permission); + } + + public boolean hasPermission(String s) { + return player.hasPermission(s); + } + + public boolean hasPermission(Permission permission) { + return player.hasPermission(permission); + } + + public PermissionAttachment addAttachment(Plugin plugin, String s, boolean b) { + return player.addAttachment(plugin, s, b); + } + + public PermissionAttachment addAttachment(Plugin plugin) { + return player.addAttachment(plugin); + } + + public PermissionAttachment addAttachment(Plugin plugin, String s, boolean b, int i) { + return player.addAttachment(plugin, s, b, i); + } + + public PermissionAttachment addAttachment(Plugin plugin, int i) { + return player.addAttachment(plugin, i); + } + + public void removeAttachment(PermissionAttachment permissionAttachment) { + player.removeAttachment(permissionAttachment); + } + + public void recalculatePermissions() { + player.recalculatePermissions(); + } + + public Set getEffectivePermissions() { + return player.getEffectivePermissions(); + } + + public boolean isOp() { + return player.isOp(); + } + + public void setOp(boolean b) { + player.setOp(b); + } +} diff --git a/core/src/main/java/com/griefcraft/model/Mode.java b/core/src/main/java/com/griefcraft/model/Mode.java new file mode 100644 index 000000000..99a83fcbc --- /dev/null +++ b/core/src/main/java/com/griefcraft/model/Mode.java @@ -0,0 +1,74 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.model; + +import org.bukkit.entity.Player; + +public class Mode { + + /** + * The name of this mode + */ + private String name; + + /** + * The player this mode belongs to + */ + private Player player; + + /** + * Mode data + */ + private String data; + + public String getName() { + return name; + } + + public Player getPlayer() { + return player; + } + + public String getData() { + return data; + } + + public void setName(String name) { + this.name = name; + } + + public void setPlayer(Player player) { + this.player = player; + } + + public void setData(String data) { + this.data = data; + } + +} diff --git a/core/src/main/java/com/griefcraft/model/Permission.java b/core/src/main/java/com/griefcraft/model/Permission.java new file mode 100644 index 000000000..b9f72e6ab --- /dev/null +++ b/core/src/main/java/com/griefcraft/model/Permission.java @@ -0,0 +1,240 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.model; + +import com.griefcraft.util.Colors; +import com.griefcraft.util.StringUtil; +import com.griefcraft.util.UUIDRegistry; +import org.json.simple.JSONObject; + +public class Permission { + + /** + * The access level + * The ordering of this enum MUST NOT change as ordinal values are used internally. + */ + public enum Access { + + /** + * The player has no access + */ + NONE, + + /** + * The player has rights that of a regular player + */ + PLAYER, + + /** + * The player has admin rights + */ + ADMIN; + + @Override + public String toString() { + return StringUtil.capitalizeFirstLetter(super.toString()); + } + } + + /** + * The type this permission applies to. + * The ordering of this enum MUST NOT change as ordinal values are used internally. + */ + public enum Type { + /** + * Applies to a specific group of players + */ + GROUP, + + /** + * Applies to a specific player + */ + PLAYER, + + /** + * Unused / reserved, has been used before + */ + RESERVED, + + /** + * Applies to citizens of a Towny town + */ + TOWN, + + /** + * Allows a specific item (such as a key) to open the protection when interacted with in hand + */ + ITEM, + + /** + * Applies to members of a WorldGuard region + */ + REGION; + + @Override + public String toString() { + return StringUtil.capitalizeFirstLetter(super.toString()); + } + } + + /** + * The entity this applies to + */ + private String name; + + /** + * The type of access used for the permission + */ + private Type type; + + /** + * The access the permission has to the protection + */ + private Access access = Access.PLAYER; + + /** + * If the permission is not synchronized to the database + */ + private boolean isVolatile = false; + + public Permission() { + } + + public Permission(String name) { + this.name = name; + } + + public Permission(String name, Type type) { + this(name); + this.type = type; + } + + public Permission(String name, Type type, Access access) { + this(name, type); + this.access = access; + } + + /** + * Encode the Permission object to a JSONObject + * + * @return + */ + public JSONObject encodeToJSON() { + JSONObject object = new JSONObject(); + + object.put("name", name); + object.put("type", getType().ordinal()); + object.put("rights", getAccess().ordinal()); + + return object; + } + + /** + * Decode a JSONObject into a Permission object + * + * @param node + * @return + */ + public static Permission decodeJSON(JSONObject node) { + Permission permission = new Permission(); + + Access access = Access.values()[((Long) node.get("rights")).intValue()]; + if (access.ordinal() == 0) { + access = Access.PLAYER; + } + + // The values are stored as longs internally, despite us passing an int + permission.setName((String) node.get("name")); + permission.setType(Type.values()[((Long) node.get("type")).intValue()]); + permission.setAccess(access); + + return permission; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(Colors.Yellow); + if (type == Type.PLAYER) { + builder.append(UUIDRegistry.formatPlayerName(getName())); + } else { + builder.append(getName()); + } + builder.append(Colors.White); + builder.append(" ("); + builder.append(Colors.Green); + builder.append(getType()); + builder.append(Colors.White); + builder.append(") "); + + if (getAccess() == Access.ADMIN) { + builder.append(Colors.White); + builder.append("("); + builder.append(Colors.Red); + builder.append("ADMIN"); + builder.append(Colors.White); + builder.append(")"); + } + return builder.toString(); + // return String.format("Permission = { protection=%d name=%s rights=%d type=%s }", protectionId, name, rights, typeToString(rights)); + } + + public String getName() { + return name; + } + + public Access getAccess() { + return access; + } + + public Type getType() { + return type; + } + + public void setName(String name) { + this.name = name; + } + + public void setAccess(Access access) { + this.access = access; + } + + public void setType(Type type) { + this.type = type; + } + + public void setVolatile(boolean isVolatile) { + this.isVolatile = isVolatile; + } + + public boolean isVolatile() { + return isVolatile; + } + +} diff --git a/core/src/main/java/com/griefcraft/model/Protection.java b/core/src/main/java/com/griefcraft/model/Protection.java new file mode 100644 index 000000000..f56e7d22a --- /dev/null +++ b/core/src/main/java/com/griefcraft/model/Protection.java @@ -0,0 +1,984 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.model; + +import com.griefcraft.cache.ProtectionCache; +import com.griefcraft.lwc.LWC; +import com.griefcraft.scripting.event.LWCProtectionRemovePostEvent; +import com.griefcraft.util.Colors; +import com.griefcraft.util.ProtectionFinder; +import com.griefcraft.util.StringUtil; +import com.griefcraft.util.TimeUtil; +import com.griefcraft.util.UUIDRegistry; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class Protection { + + /** + * The protection type + * + *

Ordering must NOT change as ordinal values are used

+ */ + public enum Type { + + /** + * The protection is usable by anyone; the most common use would include community chests + * where anyone can use the chest but no one should be able to protect as their own. + */ + PUBLIC, + + /** + * The owner (and anyone else) must enter a set password entered onto the chest in order + * to be able to access it. Entering the correct password allows them to use the chest + * until they log out or the protection is removed. + */ + PASSWORD, + + /** + * The protection is only usable by the player who created it. Further access can be + * given to players, groups, and even more specific entities + * such as Towns in the "Towny" plugin, or access lists via the "Lists" plugin + */ + PRIVATE, + + /** + * Reserved / unused, to keep ordinal order + */ + RESERVED1, + + /** + * Reserved / unused, to keep ordinal order + */ + RESERVED2, + + /** + * Allows players to deposit items into + */ + DONATION; + + /** + * Match a protection type using its string form + * + * @param text + * @return + */ + public static Type matchType(String text) { + for (Type type : values()) { + if (type.toString().equalsIgnoreCase(text)) { + return type; + } + } + + throw new IllegalArgumentException("No Protection Type found for given type: " + text); + } + + } + + /** + * All of the history items associated with this protection + */ + private final Set historyCache = new HashSet(); + + /** + * List of the permissions rights for the protection + */ + private final Set permissions = new HashSet(); + + /** + * List of flags enabled on the protection + */ + private final Map flags = new HashMap(); + + /** + * The block id + */ + private int blockId; + + /** + * The password for the chest + */ + private String password; + + /** + * JSON data for the protection + */ + private final JSONObject data = new JSONObject(); + + /** + * Unique id (in sql) + */ + private int id; + + /** + * The owner of the chest + */ + private String owner; + + /** + * The protection type + */ + private Type type; + + /** + * The world the protection is in + */ + private String world; + + /** + * The x coordinate + */ + private int x; + + /** + * The y coordinate + */ + private int y; + + /** + * The z coordinate + */ + private int z; + + /** + * The timestamp of when the protection was last accessed + */ + private long lastAccessed; + + /** + * The time the protection was created + */ + private String creation; + + /** + * Immutable flag for the protection. When removed, this bool is switched to true and any setters + * will no longer work. However, everything is still intact and in memory at this point (for now.) + */ + private boolean removed = false; + + /** + * If the protection is pending removal. Only used internally. + */ + private boolean removing = false; + + /** + * True when the protection has been modified and should be saved + */ + private boolean modified = false; + + /** + * The protection finder used to find this protection + */ + private ProtectionFinder finder; + + /** + * The block the protection is at. Saves world calls and allows better concurrency + */ + private Block cachedBlock; + + @Override + public boolean equals(Object object) { + if (!(object instanceof Protection)) { + return false; + } + + Protection other = (Protection) object; + + return id == other.id && x == other.x && y == other.y && z == other.z && (owner != null && owner.equals(other.owner)) && + (world != null && world.equals(other.world)); + } + + @Override + public int hashCode() { + int hash = 17; + + // the identifier is normally unique, but in SQLite ids may be quickly reused so we use other data + hash *= 37 + id; + + // coordinates + hash *= 37 + x; + hash *= 37 + y; + hash *= 37 + z; + + // and for good measure, to *guarantee* no collisions + if (creation != null) { + hash *= 37 + creation.hashCode(); + } + + return hash; + } + + /** + * Convert the protection to use UUIDs + * + * @return true if the protection required conversion and conversions were done + */ + public boolean convertPlayerNamesToUUIDs() { + if (!needsUUIDConversion()) { + return false; + } + + boolean res = false; + + if (!UUIDRegistry.isValidUUID(owner)) { + UUID uuid = UUIDRegistry.getUUID(owner); + + if (uuid != null) { + setOwner(uuid.toString()); + res = true; + } + } + + for (Permission permission : permissions) { + if (permission.getType() == Permission.Type.PLAYER && !UUIDRegistry.isValidUUID(permission.getName())) { + UUID uuid = UUIDRegistry.getUUID(permission.getName()); + + if (uuid != null) { + permission.setName(uuid.toString()); + modified = true; + res = true; + } + } + } + + return res; + } + + /** + * Check if this protection requires conversion from plain player names to UUIDs + * + * @return true if the protection requires conversion + */ + public boolean needsUUIDConversion() { + if (!UUIDRegistry.isValidUUID(owner)) { + return true; + } + + for (Permission permission : permissions) { + if (permission.getType() == Permission.Type.PLAYER && !UUIDRegistry.isValidUUID(permission.getName())) { + return true; + } + } + + return false; + } + + /** + * Get a formatted version of the owner's name. If the owner is a UUID and the UUID is unknown, then + * "Unknown (uuid)" will be returned. + * + * @return + */ + public String getFormattedOwnerPlayerName() { + return UUIDRegistry.formatPlayerName(owner); + } + + /** + * Encode the AccessRights to JSON + * + * @return + */ + public void encodeRights() { + // create the root + JSONArray root = new JSONArray(); + + // add all of the permissions to the root + for (Permission permission : permissions) { + if (permission != null) { + root.add(permission.encodeToJSON()); + } + } + + data.put("rights", root); + } + + /** + * Encode the protection flags to JSON + */ + public void encodeFlags() { + JSONArray root = new JSONArray(); + + for (Flag flag : flags.values()) { + if (flag != null) { + root.add(flag.getData()); + } + } + + data.put("flags", root); + } + + /** + * Ensure a history object is located in our cache + * + * @param history + */ + public void checkHistory(History history) { + if (!historyCache.contains(history)) { + historyCache.add(history); + } + } + + /** + * Check if a player has owner access to the protection + * + * @param player + * @return + */ + public boolean isOwner(Player player) { + LWC lwc = LWC.getInstance(); + + if (isRealOwner(player)) { + return true; + } else { + return lwc.isAdmin(player); + } + } + + /** + * Check if a player is the real owner to the protection + * + * @param player + * @return + */ + public boolean isRealOwner(Player player) { + if (player == null) { + return false; + } + + if (UUIDRegistry.isValidUUID(owner)) { + return UUID.fromString(owner).equals(player.getUniqueId()); + } else { + return owner.equalsIgnoreCase(player.getName()); + } + } + + /** + * Create a History object that is attached to this protection + * + * @return + */ + public History createHistoryObject() { + History history = new History(); + + history.setProtectionId(id); + history.setProtection(this); + history.setStatus(History.Status.INACTIVE); + history.setX(x); + history.setY(y); + history.setZ(z); + + // add it to the cache + historyCache.add(history); + + return history; + } + + /** + * @return the related history for this protection, which is immutable + */ + public Set getRelatedHistory() { + // cache the database's history if we don't have any yet + if (historyCache.size() == 0) { + historyCache.addAll(LWC.getInstance().getPhysicalDatabase().loadHistory(this)); + } + + // now we can return an immutable cache + return Collections.unmodifiableSet(historyCache); + } + + /** + * Get the related history for this protection using the given type + * + * @param type + * @return + */ + public List getRelatedHistory(History.Type type) { + List matches = new ArrayList(); + Set relatedHistory = getRelatedHistory(); + + for (History history : relatedHistory) { + if (history.getType() == type) { + matches.add(history); + } + } + + return matches; + } + + /** + * Check if a flag is enabled + * + * @param type + * @return + */ + public boolean hasFlag(Flag.Type type) { + return flags.containsKey(type); + } + + /** + * Get the enabled flag for the corresponding type + * + * @param type + * @return + */ + public Flag getFlag(Flag.Type type) { + return flags.get(type); + } + + /** + * Add a flag to the protection + * + * @param flag + * @return + */ + public boolean addFlag(Flag flag) { + if (removed || flag == null) { + return false; + } + + if (!flags.containsKey(flag.getType())) { + flags.put(flag.getType(), flag); + modified = true; + return true; + } + + return false; + } + + /** + * Remove a flag from the protection + * + * @param flag + * @return + */ + public void removeFlag(Flag flag) { + if (removed) { + return; + } + + flags.remove(flag.getType()); + this.modified = true; + } + + /** + * Check if the entity + permissions type exists, and if so return the rights (-1 if it does not exist) + * + * @param type + * @param name + * @return the permissions the player has + */ + public Permission.Access getAccess(String name, Permission.Type type) { + for (Permission permission : permissions) { + if (permission.getType() == type && permission.getName().equalsIgnoreCase(name)) { + return permission.getAccess(); + } + } + + return Permission.Access.NONE; + } + + /** + * @return the list of permissions + */ + public List getPermissions() { + return Collections.unmodifiableList(new ArrayList(permissions)); + } + + /** + * Remove temporary permissions rights from the protection + */ + public void removeTemporaryPermissions() { + Iterator iter = permissions.iterator(); + + while (iter.hasNext()) { + Permission permission = iter.next(); + + if (permission.isVolatile()) { + iter.remove(); + } + } + } + + /** + * Add an permission to the protection + * + * @param permission + */ + public void addPermission(Permission permission) { + if (removed || permission == null) { + return; + } + + // remove any other rights with the same identity + removePermissions(permission.getName(), permission.getType()); + + // now we can safely add it + permissions.add(permission); + modified = true; + } + + /** + * Remove permissions from the protection that match a name AND type + * + * @param name + * @param type + */ + public void removePermissions(String name, Permission.Type type) { + if (removed) { + return; + } + + Iterator iter = permissions.iterator(); + + while (iter.hasNext()) { + Permission permission = iter.next(); + + if ((permission.getName().equals(name) || name.equals("*")) && permission.getType() == type) { + iter.remove(); + modified = true; + } + } + } + + /** + * Remove all of the permissions + */ + public void removeAllPermissions() { + permissions.clear(); + modified = true; + } + + /** + * Checks if the protection has the correct block in the world + * + * @return + */ + public boolean isBlockInWorld() { + int storedBlockId = getBlockId(); + Block block = getBlock(); + + switch (block.getType()) { + case FURNACE: + case BURNING_FURNACE: + return storedBlockId == Material.FURNACE.getId() || storedBlockId == Material.BURNING_FURNACE.getId(); + + case STEP: + case DOUBLE_STEP: + return storedBlockId == Material.STEP.getId() || storedBlockId == Material.DOUBLE_STEP.getId(); + + default: + return storedBlockId == block.getTypeId(); + } + } + + public JSONObject getData() { + return data; + } + + public int getBlockId() { + return blockId; + } + + public String getPassword() { + return password; + } + + public String getCreation() { + return creation; + } + + public int getId() { + return id; + } + + public String getOwner() { + return owner; + } + + public Type getType() { + return type; + } + + public String getWorld() { + return world; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public long getLastAccessed() { + return lastAccessed; + } + + public void setBlockId(int blockId) { + if (removed) { + return; + } + + this.blockId = blockId; + this.modified = true; + } + + public void setPassword(String password) { + if (removed) { + return; + } + + this.password = password; + this.modified = true; + } + + public void setCreation(String creation) { + if (removed) { + return; + } + + this.creation = creation; + this.modified = true; + } + + public void setId(int id) { + if (removed) { + return; + } + + this.id = id; + this.modified = true; + } + + public void setOwner(String owner) { + if (removed) { + return; + } + + this.owner = owner; + this.modified = true; + } + + public void setType(Type type) { + if (removed) { + return; + } + + this.type = type; + this.modified = true; + } + + public void setWorld(String world) { + if (removed) { + return; + } + + this.world = world; + this.modified = true; + } + + public void setX(int x) { + if (removed) { + return; + } + + this.x = x; + this.modified = true; + } + + public void setY(int y) { + if (removed) { + return; + } + + this.y = y; + this.modified = true; + } + + public void setZ(int z) { + if (removed) { + return; + } + + this.z = z; + this.modified = true; + } + + public void setLastAccessed(long lastAccessed) { + if (removed) { + return; + } + + this.lastAccessed = lastAccessed; + this.modified = true; + } + + /** + * Sets the protection finder used to create this protection + * + * @param finder + */ + public void setProtectionFinder(ProtectionFinder finder) { + this.finder = finder; + } + + /** + * Gets the protection finder used the create this protection + * + * @return the ProtectionFinder used to create this protection + */ + public ProtectionFinder getProtectionFinder() { + return finder; + } + + /** + * Remove the protection from the database + */ + public void remove() { + if (removed) { + return; + } + + LWC lwc = LWC.getInstance(); + removeTemporaryPermissions(); + + // we're removing it, so assume there are no changes + modified = false; + removing = true; + + // broadcast the removal event + // we broadcast before actually removing to give them a chance to use any password that would be removed otherwise + lwc.getModuleLoader().dispatchEvent(new LWCProtectionRemovePostEvent(this)); + + // mark related transactions as inactive + for (History history : getRelatedHistory(History.Type.TRANSACTION)) { + if (history.getStatus() != History.Status.ACTIVE) { + continue; + } + + history.setStatus(History.Status.INACTIVE); + } + + // ensure all history objects for this protection are saved + checkAndSaveHistory(); + + // make the protection immutable + removed = true; + + // and now finally remove it from the database + lwc.getDatabaseThread().removeProtection(this); + lwc.getPhysicalDatabase().removeProtection(id); + removeCache(); + } + + /** + * Remove the protection from cache + */ + public void removeCache() { + LWC lwc = LWC.getInstance(); + lwc.getProtectionCache().removeProtection(this); + radiusRemoveCache(); + } + + /** + * Remove blocks around the protection in a radius of 3, to account for broken known / null blocks + */ + public void radiusRemoveCache() { + ProtectionCache cache = LWC.getInstance().getProtectionCache(); + + for (int x = -3; x <= 3; x++) { + for (int y = -3; y <= 3; y++) { + for (int z = -3; z <= 3; z++) { + String cacheKey = world + ":" + (this.x + x) + ":" + (this.y + y) + ":" + (this.z + z); + + // get the protection for that entry + Protection protection = cache.getProtection(cacheKey); + + // the ifnull compensates for the block being in the null cache. It will remove it from that. + if ((protection != null && id == protection.getId()) || protection == null) { + cache.remove(cacheKey); + } + } + } + } + } + + /** + * Queue the protection to be saved + */ + public void save() { + if (removed) { + return; + } + + LWC.getInstance().getDatabaseThread().addProtection(this); + } + + /** + * Force a protection update to the live database + */ + public void saveNow() { + if (removed) { + return; + } + + // encode JSON objects + encodeRights(); + encodeFlags(); + + // only save the protection if it was modified + if (modified && !removing) { + LWC.getInstance().getPhysicalDatabase().saveProtection(this); + } + + // check the cache for history updates + checkAndSaveHistory(); + } + + /** + * Saves any of the history items for the Protection that have been modified + */ + public void checkAndSaveHistory() { + if (removed) { + return; + } + + for (History history : getRelatedHistory()) { + // if the history object was modified we need to save it + if (history.wasModified()) { + history.saveNow(); + } + } + } + + /** + * @return the key used for the protection cache + */ + public String getCacheKey() { + return world + ":" + x + ":" + y + ":" + z; + } + + /** + * @return the Bukkit world the protection should be located in + */ + public World getBukkitWorld() { + if (world == null || world.isEmpty()) { + return Bukkit.getServer().getWorlds().get(0); + } + + return Bukkit.getServer().getWorld(world); + } + + /** + * @return the Bukkit Player object of the owner + */ + public Player getBukkitOwner() { + return Bukkit.getServer().getPlayer(owner); + } + + /** + * @return the block representing the protection in the world + */ + public Block getBlock() { + if (cachedBlock != null) { + return cachedBlock; + } + + World world = getBukkitWorld(); + + if (world == null) { + return null; + } + + cachedBlock = world.getBlockAt(x, y, z); + return cachedBlock; + } + + /** + * @return + */ + @Override + public String toString() { + // format the flags prettily + String flagStr = ""; + + for (Flag flag : flags.values()) { + flagStr += flag.toString() + ","; + } + + if (flagStr.endsWith(",")) { + flagStr = flagStr.substring(0, flagStr.length() - 1); + } + + // format the last accessed time + String lastAccessed = TimeUtil.timeToString((System.currentTimeMillis() / 1000L) - this.lastAccessed); + + if (!lastAccessed.equals("Not yet known")) { + lastAccessed += " ago"; + } + + return String.format("%s %s" + Colors.White + " " + Colors.Green + "Id=%d Owner=%s Location=[%s %d,%d,%d] Created=%s Flags=%s LastAccessed=%s", typeToString(), (blockId > 0 ? (LWC.materialToString(blockId)) : "Not yet cached"), id, owner, world, x, y, z, creation, flagStr, lastAccessed); + } + + /** + * @return string representation of the protection type + */ + public String typeToString() { + return StringUtil.capitalizeFirstLetter(type.toString()); + } + + /** + * Updates the protection in the protection cache + */ + @Deprecated + public void update() { + throw new UnsupportedOperationException("Protection.update() is no longer necessary!"); + } + +} diff --git a/core/src/main/java/com/griefcraft/modules/admin/AdminBackup.java b/core/src/main/java/com/griefcraft/modules/admin/AdminBackup.java new file mode 100644 index 000000000..6f380c92f --- /dev/null +++ b/core/src/main/java/com/griefcraft/modules/admin/AdminBackup.java @@ -0,0 +1,92 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.modules.admin; + +import com.griefcraft.io.Backup; +import com.griefcraft.io.BackupManager; +import com.griefcraft.lwc.LWC; +import com.griefcraft.scripting.JavaModule; +import com.griefcraft.scripting.event.LWCCommandEvent; +import com.griefcraft.util.StringUtil; +import org.bukkit.command.CommandSender; + +public class AdminBackup extends JavaModule { + + @Override + public void onCommand(LWCCommandEvent event) { + if (event.isCancelled()) { + return; + } + + if (!event.hasFlag("a", "admin")) { + return; + } + + final LWC lwc = event.getLWC(); + final CommandSender sender = event.getSender(); + String[] args = event.getArgs(); + + if (!args[0].equals("backup")) { + return; + } + + // we have the right command + event.setCancelled(true); + + if (args.length == 1) { + lwc.sendSimpleUsage(sender, "/lwc admin backup "); + return; + } + + // The action they want to perform + String action = args[1].toLowerCase(); + + if (action.equals("create")) { + // Dumb code for now + Backup created = lwc.getBackupManager().createBackup(); + sender.sendMessage("Backup is being created now."); + } else if (action.equals("restore")) { + if (args.length < 3) { + lwc.sendSimpleUsage(sender, "/lwc admin backup restore "); + return; + } + + final String backupName = StringUtil.join(args, 2); + sender.sendMessage("Restoring backup " + backupName); + + lwc.getPlugin().getServer().getScheduler().scheduleAsyncDelayedTask(lwc.getPlugin(), new Runnable() { + public void run() { + BackupManager.Result result = lwc.getBackupManager().restoreBackup(backupName); + sender.sendMessage("Result: " + result); + } + }); + } + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/griefcraft/modules/admin/AdminCache.java b/core/src/main/java/com/griefcraft/modules/admin/AdminCache.java new file mode 100644 index 000000000..77a1f7872 --- /dev/null +++ b/core/src/main/java/com/griefcraft/modules/admin/AdminCache.java @@ -0,0 +1,77 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.modules.admin; + +import com.griefcraft.cache.ProtectionCache; +import com.griefcraft.lwc.LWC; +import com.griefcraft.scripting.JavaModule; +import com.griefcraft.scripting.event.LWCCommandEvent; +import com.griefcraft.util.Colors; +import org.bukkit.command.CommandSender; + +public class AdminCache extends JavaModule { + + @Override + public void onCommand(LWCCommandEvent event) { + if (event.isCancelled()) { + return; + } + + if (!event.hasFlag("a", "admin")) { + return; + } + + LWC lwc = event.getLWC(); + CommandSender sender = event.getSender(); + String[] args = event.getArgs(); + + if (!args[0].equals("cache")) { + return; + } + + // we have the right command + event.setCancelled(true); + ProtectionCache cache = lwc.getProtectionCache(); + + if (args.length > 1) { + String cmd = args[1].toLowerCase(); + + if (cmd.equals("clear")) { + cache.clear(); + lwc.sendLocale(sender, "lwc.admin.caches.cleared"); + } + } else { + int size = cache.size(); + int capacity = cache.capacity(); + + sender.sendMessage(Colors.Green + size + Colors.Yellow + "/" + Colors.Green + capacity); + } + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/griefcraft/modules/admin/AdminCleanup.java b/core/src/main/java/com/griefcraft/modules/admin/AdminCleanup.java new file mode 100644 index 000000000..c312c9226 --- /dev/null +++ b/core/src/main/java/com/griefcraft/modules/admin/AdminCleanup.java @@ -0,0 +1,255 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.modules.admin; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.model.Protection; +import com.griefcraft.scripting.JavaModule; +import com.griefcraft.scripting.event.LWCCommandEvent; +import com.griefcraft.sql.Database; +import com.griefcraft.sql.PhysDB; +import com.griefcraft.util.Colors; +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitScheduler; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; + +public class AdminCleanup extends JavaModule { + + /** + * The amount of protection block gets to batch at once + */ + private static int BATCH_SIZE = 250; + + @Override + public void onCommand(LWCCommandEvent event) { + if (event.isCancelled()) { + return; + } + + if (!event.hasFlag("a", "admin")) { + return; + } + + LWC lwc = event.getLWC(); + CommandSender sender = event.getSender(); + String[] args = event.getArgs(); + + if (!args[0].equals("cleanup")) { + return; + } + + // we have the right command + event.setCancelled(true); + + // if we shouldn't output + boolean silent = false; + + if (args.length > 1 && args[1].equalsIgnoreCase("silent")) { + silent = true; + } + + lwc.sendLocale(sender, "protection.admin.cleanup.start", "count", lwc.getPhysicalDatabase().getProtectionCount()); + + // do the work in a separate thread so we don't fully lock the server + // new Thread(new Admin_Cleanup_Thread(lwc, sender)).start(); + Bukkit.getScheduler().scheduleAsyncDelayedTask(lwc.getPlugin(), new Admin_Cleanup_Thread(lwc, sender, silent)); + } + + /** + * Class that handles cleaning up the LWC database usage: /lwc admin cleanup + */ + private static class Admin_Cleanup_Thread implements Runnable { + + private LWC lwc; + private CommandSender sender; + private boolean silent; + + public Admin_Cleanup_Thread(LWC lwc, CommandSender sender, boolean silent) { + this.lwc = lwc; + this.sender = sender; + this.silent = silent; + } + + /** + * Push removal changes to the database + * + * @param toRemove + */ + public void push(List toRemove) throws SQLException { + final StringBuilder builder = new StringBuilder(); + final int total = toRemove.size(); + int count = 0; + + // iterate over the items to remove + Iterator iter = toRemove.iterator(); + + // the database prefix + String prefix = lwc.getPhysicalDatabase().getPrefix(); + + // create the statement to use + Statement statement = lwc.getPhysicalDatabase().getConnection().createStatement(); + + while (iter.hasNext()) { + int protectionId = iter.next(); + + if (count % 100000 == 0) { + builder.append("DELETE FROM ").append(prefix).append("protections WHERE id IN (").append(protectionId); + } else { + builder.append(",").append(protectionId); + } + + if (count % 100000 == 99999 || count == (total - 1)) { + builder.append(")"); + statement.executeUpdate(builder.toString()); + builder.setLength(0); + + sender.sendMessage(Colors.Green + "REMOVED " + (count + 1) + " / " + total); + } + + count++; + } + + statement.close(); + } + + public void run() { + List toRemove = new LinkedList(); + int removed = 0; + int percentChecked = 0; + + // the bukkit scheduler + BukkitScheduler scheduler = Bukkit.getScheduler(); + + try { + sender.sendMessage(Colors.Red + "Processing cleanup request now in a separate thread"); + + // the list of protections work off of. We batch updates to the world + // so we can more than 20 results/second. + final List protections = new ArrayList(BATCH_SIZE); + + // amount of protections + int totalProtections = lwc.getPhysicalDatabase().getProtectionCount(); + + // TODO separate stream logic to somewhere else :) + // Create a new database connection, we are just reading + PhysDB database = new PhysDB(); + database.connect(); + database.load(); + + Statement resultStatement = database.getConnection().createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + + if (lwc.getPhysicalDatabase().getType() == Database.Type.MySQL) { + resultStatement.setFetchSize(Integer.MIN_VALUE); + } + + String prefix = lwc.getPhysicalDatabase().getPrefix(); + ResultSet result = resultStatement.executeQuery("SELECT id, owner, type, x, y, z, data, blockId, world, password, date, last_accessed FROM " + prefix + "protections"); + int checked = 0; + + while (result.next()) { + final Protection tprotection = database.resolveProtection(result); + + if (protections.size() != BATCH_SIZE) { + // Wait until we have BATCH_SIZE protections + protections.add(tprotection); + + if (protections.size() != totalProtections) { + continue; + } + } + + // Get all of the blocks in the world + Future getBlocks = scheduler.callSyncMethod(lwc.getPlugin(), new Callable() { + public Void call() throws Exception { + for (Protection protection : protections) { + protection.getBlock(); // this will cache it also :D + } + + return null; + } + }); + + // Get all of the blocks + getBlocks.get(); + + for (Protection protection : protections) { + Block block = protection.getBlock(); + + // remove protections not found in the world + if (block == null || !lwc.isProtectable(block)) { + toRemove.add(protection.getId()); + removed ++; + + if (!silent) { + lwc.sendLocale(sender, "protection.admin.cleanup.removednoexist", "protection", protection.toString()); + } + } + + checked ++; + } + + // percentage dump + int percent = (int) ((((double) checked) / totalProtections) * 100); + + if (percent % 5 == 0 && percentChecked != percent) { + percentChecked = percent; + sender.sendMessage(Colors.Red + "Cleanup @ " + percent + "% [ " + checked + "/" + totalProtections + " protections ] [ removed " + removed + " protections ]"); + } + + // Clear the protection set, we are done with them + protections.clear(); + } + + // close the sql statements + result.close(); + resultStatement.close(); + + // flush all of the queries + push(toRemove); + + sender.sendMessage("Cleanup completed. Removed " + removed + " protections out of " + checked + " checked protections."); + } catch (Exception e) { // database.connect() throws Exception + System.out.println("Exception caught during cleanup: " + e.getMessage()); + } + } + + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/griefcraft/modules/admin/AdminClear.java b/core/src/main/java/com/griefcraft/modules/admin/AdminClear.java new file mode 100644 index 000000000..4ae0cd088 --- /dev/null +++ b/core/src/main/java/com/griefcraft/modules/admin/AdminClear.java @@ -0,0 +1,73 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.modules.admin; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.scripting.JavaModule; +import com.griefcraft.scripting.event.LWCCommandEvent; +import org.bukkit.command.CommandSender; + +public class AdminClear extends JavaModule { + + @Override + public void onCommand(LWCCommandEvent event) { + if (event.isCancelled()) { + return; + } + + if (!event.hasFlag("a", "admin")) { + return; + } + + LWC lwc = event.getLWC(); + CommandSender sender = event.getSender(); + String[] args = event.getArgs(); + + if (!args[0].equals("clear")) { + return; + } + + // we have the right command + event.setCancelled(true); + + if (args.length < 2) { + lwc.sendSimpleUsage(sender, "/lwc admin clear "); + return; + } + + String toClear = args[1].toLowerCase(); + + if (toClear.equals("protections")) { + lwc.getPhysicalDatabase().removeAllProtections(); + } + + lwc.sendLocale(sender, "protection.admin.clear." + toClear); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/griefcraft/modules/admin/AdminDump.java b/core/src/main/java/com/griefcraft/modules/admin/AdminDump.java new file mode 100644 index 000000000..f102c0f3d --- /dev/null +++ b/core/src/main/java/com/griefcraft/modules/admin/AdminDump.java @@ -0,0 +1,119 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.modules.admin; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.scripting.JavaModule; +import com.griefcraft.scripting.ModuleLoader; +import com.griefcraft.scripting.event.LWCCommandEvent; +import org.bukkit.command.CommandSender; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class AdminDump extends JavaModule { + + @Override + public void onCommand(LWCCommandEvent event) { + if (event.isCancelled()) { + return; + } + + if (!event.hasFlag("a", "admin")) { + return; + } + + LWC lwc = event.getLWC(); + CommandSender sender = event.getSender(); + String[] args = event.getArgs(); + + if (!args[0].equals("dump")) { + return; + } + + // we have the right command + event.setCancelled(true); + + // get what should be dumped + if (args.length < 2) { + lwc.sendSimpleUsage(sender, "/lwc admin dump "); + return; + } + + String toDump = args[1].toLowerCase(); + + if (toDump.equals("locale")) { + // check if the file already exists + File localeFile = new File(ModuleLoader.ROOT_PATH + "locale/lwc.properties"); + + if (localeFile.exists()) { + lwc.sendLocale(sender, "lwc.admin.dump.fileexists", "file", localeFile.getPath()); + return; + } + + // let's create the locale folder if needed + new File(ModuleLoader.ROOT_PATH + "locale/").mkdir(); + + try { + localeFile.createNewFile(); + + // output stream + OutputStream outputStream = new FileOutputStream(localeFile); + + // input stream + InputStream inputStream = getClass().getResourceAsStream("/lang/lwc_" + lwc.getPlugin().getCurrentLocale() + ".properties"); + + if (inputStream == null) { + lwc.sendLocale(sender, "lwc.admin.dump.filenotfound"); + return; + } + + // begin copying + byte[] buffer = new byte[4096]; + int length; + + while ((length = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, length); + } + + // cool beans! + outputStream.close(); + inputStream.close(); + + lwc.sendLocale(sender, "lwc.admin.dump.success", "file", localeFile.getAbsolutePath()); + } catch (IOException e) { + sender.sendMessage("Error: " + e.getMessage()); + } + } + } + +} diff --git a/core/src/main/java/com/griefcraft/modules/admin/AdminExpire.java b/core/src/main/java/com/griefcraft/modules/admin/AdminExpire.java new file mode 100644 index 000000000..73fbd4a16 --- /dev/null +++ b/core/src/main/java/com/griefcraft/modules/admin/AdminExpire.java @@ -0,0 +1,89 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.griefcraft.modules.admin; + +import com.griefcraft.lwc.LWC; +import com.griefcraft.scripting.JavaModule; +import com.griefcraft.scripting.event.LWCCommandEvent; +import com.griefcraft.util.StringUtil; +import com.griefcraft.util.TimeUtil; +import org.bukkit.command.CommandSender; + +public class AdminExpire extends JavaModule { + + @Override + public void onCommand(LWCCommandEvent event) { + if (event.isCancelled()) { + return; + } + + if (!event.hasFlag("a", "admin")) { + return; + } + + LWC lwc = event.getLWC(); + CommandSender sender = event.getSender(); + String[] args = event.getArgs(); + + if (!args[0].equals("expire")) { + return; + } + + // we have the right command + event.setCancelled(true); + + if (args.length < 2) { + lwc.sendSimpleUsage(sender, "/lwc admin expire