diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index 319f358..7e95224 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ -![2022-08-15_02 51 28](https://user-images.githubusercontent.com/70987828/184551011-7da1dca5-faab-473c-b6a5-d2489b135ca9.png) - -# This project is currently under recoding - # Custom-Crops -StardewValley Like Farming System +![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/Xiao-MoMi/Custom-Crops) +![bStats Servers](https://img.shields.io/bstats/servers/16593) +![bStats Players](https://img.shields.io/bstats/players/16593) +![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Crops) +[![](https://jitpack.io/v/Xiao-MoMi/Custom-Crops.svg)](https://jitpack.io/#Xiao-MoMi/Custom-Crops) + +Gitbook + + +Ultra-customizable planting experience for Minecraft servers ### Support the developer @@ -69,41 +74,3 @@ dependencies { compileOnly("com.github.Xiao-MoMi:Custom-Crops:{LATEST}") } ``` - -### API Guide -```access transformers -public class YourClass { - - private CustomCropsAPI api; - - public YourClass() { - api = CustomCropsAPI.getInstance(); - } - - public void yourMethod() { - api.xxx(); - } -} -``` - -#### Events -``` -CropBreakEvent -CropInteractEvent -CropPlantEvent -FertilizerUseEvent -GreenhouseGlassBreakEvent -GreenhouseGlassPlaceEvent -PotBreakEvent -PotInfoEvent -PotInteractEvent -PotPlaceEvent -PotWaterEvent -ScarecrowBreakEvent -ScarecrowPlaceEvent -SprinklerFillEvent -SprinklerPlaceEvent -SprinklerInteractEvent -SprinklerBreakEvent -SeasonChangeEvent -``` diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 367c9bb..7aa76c7 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,3 +1,4 @@ dependencies { - compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT") + compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") + implementation("com.flowpowered:flow-nbt:2.0.2") } \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsAPI.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsAPI.java deleted file mode 100644 index dbd2596..0000000 --- a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsAPI.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api; - -import net.momirealms.customcrops.api.object.CCGrowingCrop; -import net.momirealms.customcrops.api.object.CCPot; -import net.momirealms.customcrops.api.object.CCSprinkler; -import net.momirealms.customcrops.api.object.CCWorldSeason; -import org.bukkit.Location; -import org.bukkit.World; - -import javax.annotation.Nullable; - -public interface CustomCropsAPI { - - /** - * Get the pot instance at the specified location - * In order to reduce the memory usage, pot data would be removed - * if it has no water and no fertilizer - * But if "only-work-in-loaded-chunks" is true, pot data would not be removed - * @param location location - * @return pot - */ - @Nullable - CCPot getPotAt(Location location); - - /** - * Get the on growing crop at the specified location - * It would be null if the crop already comes to its final stage - * @param location location - * @return on growing crop - */ - @Nullable - CCGrowingCrop getCropAt(Location location); - - /** - * If the block is a greenhouse glass in data - * It would return false if your greenhouse glass lost due to server crash - * @param location location - * @return whether the block is greenhouse glass - */ - boolean isGreenhouseGlass(Location location); - - /** - * If the chunk has a scarecrow - * @param location location - * @return has scarecrow or not - */ - boolean hasScarecrowInChunk(Location location); - - /** - * Get the sprinkler at the specified location - * It would be null if the sprinkler run out of water - * @param location location - * @return sprinkler - */ - @Nullable - CCSprinkler getSprinklerAt(Location location); - - /** - * Set the world's season - * @param world world - * @param season season - */ - void setSeason(String world, String season); - - /** - * Set the world's date - * @param world world - * @param date date - */ - void setDate(String world, int date); - - /** - * Add a world's date - * @param world world - */ - void addDate(String world); - - /** - * Get a world's season - * @param world world - * @return season - */ - @Nullable - CCWorldSeason getSeason(String world); - - /** - * Force the crops to grow in specified seconds - * @param world world - * @param seconds time - */ - void grow(World world, int seconds); - - /** - * Force the sprinkler to work in specified seconds - * @param world world - * @param seconds time - */ - void sprinklerWork(World world, int seconds); - - /** - * Force the pots to reduce water and consume fertilizer in specified seconds - * @param world world - * @param seconds time - */ - void consume(World world, int seconds); - - /** - * Get the api instance - * It would be null if the plugin is not enabled - * @return api - */ - @Nullable - static CustomCropsAPI getInstance() { - return CustomCropsPlugin.getInstance().getAPI(); - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java index 1998c16..215f315 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java +++ b/api/src/main/java/net/momirealms/customcrops/api/CustomCropsPlugin.java @@ -17,19 +17,107 @@ package net.momirealms.customcrops.api; +import net.momirealms.customcrops.api.manager.*; +import net.momirealms.customcrops.api.scheduler.Scheduler; import org.bukkit.plugin.java.JavaPlugin; public abstract class CustomCropsPlugin extends JavaPlugin { protected static CustomCropsPlugin instance; + protected VersionManager versionManager; + protected ConfigManager configManager; + protected Scheduler scheduler; + protected RequirementManager requirementManager; + protected ActionManager actionManager; + protected IntegrationManager integrationManager; + protected CoolDownManager coolDownManager; + protected WorldManager worldManager; + protected ItemManager itemManager; + protected AdventureManager adventure; + protected MessageManager messageManager; + protected ConditionManager conditionManager; + protected PlaceholderManager placeholderManager; - protected CustomCropsAPI customCropsAPI; - - public CustomCropsAPI getAPI() { - return customCropsAPI; + public CustomCropsPlugin() { + instance = this; } public static CustomCropsPlugin getInstance() { return instance; } + + public static CustomCropsPlugin get() { + return instance; + } + + /* Get version manager */ + public VersionManager getVersionManager() { + return versionManager; + } + + /* Get config manager */ + public ConfigManager getConfigManager() { + return configManager; + } + + /* Get scheduler */ + public Scheduler getScheduler() { + return scheduler; + } + + /* Get requirement manager */ + public RequirementManager getRequirementManager() { + return requirementManager; + } + + /* Get integration manager */ + public IntegrationManager getIntegrationManager() { + return integrationManager; + } + + /* Get action manager */ + public ActionManager getActionManager() { + return actionManager; + } + + /* Get cool down manager */ + public CoolDownManager getCoolDownManager() { + return coolDownManager; + } + + /* Get world data manager */ + public WorldManager getWorldManager() { + return worldManager; + } + + /* Get item manager */ + public ItemManager getItemManager() { + return itemManager; + } + + /* Get message manager */ + public MessageManager getMessageManager() { + return messageManager; + } + + /* Get adventure manager */ + public AdventureManager getAdventure() { + return adventure; + } + + /* Get condition manager */ + public ConditionManager getConditionManager() { + return conditionManager; + } + + /* Get placeholder manager */ + public PlaceholderManager getPlaceholderManager() { + return placeholderManager; + } + + public abstract boolean isHookedPluginEnabled(String plugin); + + public abstract void debug(String debug); + + public abstract void reload(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/SkillInterface.java b/api/src/main/java/net/momirealms/customcrops/api/common/Initable.java similarity index 78% rename from plugin/src/main/java/net/momirealms/customcrops/integration/SkillInterface.java rename to api/src/main/java/net/momirealms/customcrops/api/common/Initable.java index d69466b..792ce4e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/SkillInterface.java +++ b/api/src/main/java/net/momirealms/customcrops/api/common/Initable.java @@ -15,13 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration; +package net.momirealms.customcrops.api.common; -import org.bukkit.entity.Player; +public interface Initable { -public interface SkillInterface { + void init(); - void addXp(Player player, double amount); - - int getLevel(Player player); + void disable(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/Pair.java b/api/src/main/java/net/momirealms/customcrops/api/common/Pair.java similarity index 94% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/Pair.java rename to api/src/main/java/net/momirealms/customcrops/api/common/Pair.java index 4616c5f..1c9ac50 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/Pair.java +++ b/api/src/main/java/net/momirealms/customcrops/api/common/Pair.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.common; public record Pair(L left, R right) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/Property.java b/api/src/main/java/net/momirealms/customcrops/api/common/Property.java new file mode 100644 index 0000000..f75361b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/common/Property.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.common; + +public interface Property { + T get(); + void set(T value); +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/Reloadable.java b/api/src/main/java/net/momirealms/customcrops/api/common/Reloadable.java new file mode 100644 index 0000000..c19bc59 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/common/Reloadable.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.common; + +public interface Reloadable extends Initable { + + @Override + default void init() { + load(); + } + + void load(); + + void unload(); + + @Override + default void disable() { + unload(); + } + + default void reload() { + unload(); + load(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/Tuple.java b/api/src/main/java/net/momirealms/customcrops/api/common/Tuple.java similarity index 96% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/Tuple.java rename to api/src/main/java/net/momirealms/customcrops/api/common/Tuple.java index f2b12a0..f7789a0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/Tuple.java +++ b/api/src/main/java/net/momirealms/customcrops/api/common/Tuple.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.common; public class Tuple { diff --git a/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java b/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java new file mode 100644 index 0000000..9910df0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/common/item/EventItem.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.common.item; + +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.requirement.State; + +public interface EventItem { + + void trigger(ActionTrigger actionTrigger, State state); + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/Platform.java b/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java similarity index 87% rename from plugin/src/main/java/net/momirealms/customcrops/customplugin/Platform.java rename to api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java index c1fd4ba..18b3448 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/Platform.java +++ b/api/src/main/java/net/momirealms/customcrops/api/common/item/KeyItem.java @@ -15,9 +15,9 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.customplugin; +package net.momirealms.customcrops.api.common.item; -public enum Platform { - ItemsAdder, - Oraxen +public interface KeyItem { + + String getKey(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java index 6510e07..0159699 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropBreakEvent.java @@ -17,36 +17,33 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; import org.bukkit.Location; -import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * An event that triggered when breaking a crop */ -public class CropBreakEvent extends Event implements Cancellable { +public class CropBreakEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; - private final String cropItemID; - private final String cropKey; private final Location location; - private final Entity entity; + private final WorldCrop worldCrop; public CropBreakEvent( - @Nullable Entity entity, - @NotNull String cropKey, - @NotNull String cropItemID, - @NotNull Location location + @NotNull Player player, + @NotNull Location location, + @Nullable WorldCrop worldCrop ) { - this.entity = entity; - this.cropItemID = cropItemID; + super(player); this.location = location; - this.cropKey = cropKey; + this.worldCrop = worldCrop; } @Override @@ -71,12 +68,12 @@ public class CropBreakEvent extends Event implements Cancellable { } /** - * Get the crop item id in IA/Oraxen - * @return item id + * Get the crop's data, it might be null if it's spawned by other plugins in the wild + * @return crop data */ - @NotNull - public String getCropItemID() { - return cropItemID; + @Nullable + public WorldCrop getWorldCrop() { + return worldCrop; } /** @@ -87,22 +84,4 @@ public class CropBreakEvent extends Event implements Cancellable { public Location getLocation() { return location; } - - /** - * Would be null if the crop is not broken by an entity - * @return entity - */ - @Nullable - public Entity getEntity() { - return entity; - } - - /** - * Get the crop config key - * @return crop key - */ - @NotNull - public String getCropKey() { - return cropKey; - } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java index f3441b4..4f1fe41 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropInteractEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -24,6 +25,7 @@ import org.bukkit.event.HandlerList; import org.bukkit.event.player.PlayerEvent; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * An event that triggered when a player interacts a crop @@ -33,21 +35,18 @@ public class CropInteractEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final String cropItemID; - private final String cropKey; + private final WorldCrop crop; private final ItemStack itemInHand; public CropInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @NotNull Location location, - @NotNull String cropItemID, - @NotNull String cropKey + @Nullable WorldCrop crop ) { super(who); - this.cropItemID = cropItemID; this.location = location; - this.cropKey = cropKey; + this.crop = crop; this.itemInHand = itemInHand; } @@ -92,20 +91,11 @@ public class CropInteractEvent extends PlayerEvent implements Cancellable { } /** - * Get the crop model item id - * @return model item id + * Get the crop's data, it might be null if it's spawned by other plugins in the wild + * @return crop data */ - @NotNull - public String getCropItemID() { - return cropItemID; - } - - /** - * Get the crop config key - * @return crop key - */ - @NotNull - public String getCropKey() { - return cropKey; + @Nullable + public WorldCrop getCrop() { + return crop; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java index 479e929..2d55ab1 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CropPlantEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.item.Crop; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -33,25 +34,22 @@ public class CropPlantEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final ItemStack itemInHand; - private final String cropKey; + private final Crop crop; private final Location location; private int point; - private String cropItemID; public CropPlantEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @NotNull Location location, - @NotNull String cropKey, - int point, - @NotNull String cropItemID + @NotNull Crop crop, + int point ) { super(who); this.itemInHand = itemInHand; this.location = location; - this.cropKey = cropKey; + this.crop = crop; this.point = point; - this.cropItemID = cropItemID; } @Override @@ -85,16 +83,18 @@ public class CropPlantEvent extends PlayerEvent implements Cancellable { } /** - * Get the crop config key - * @return crop key + * Get the crop's config + * + * @return crop */ @NotNull - public String getCropKey() { - return cropKey; + public Crop getCrop() { + return crop; } /** - * Get the crop location + * Get the crop's location + * * @return location */ @NotNull @@ -105,7 +105,7 @@ public class CropPlantEvent extends PlayerEvent implements Cancellable { /** * Get the initial point * It would be 0 when planting - * but might be a value higher than 0 when replanting + * * @return point */ public int getPoint() { @@ -119,21 +119,4 @@ public class CropPlantEvent extends PlayerEvent implements Cancellable { public void setPoint(int point) { this.point = point; } - - /** - * Get the crop stage model item id - * @return crop model - */ - @NotNull - public String getCropModel() { - return cropItemID; - } - - /** - * Set the crop model item id - * @param cropItemID crop model item id - */ - public void setCropModel(String cropItemID) { - this.cropItemID = cropItemID; - } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/BukkitSchedulerImpl.java b/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java similarity index 51% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/BukkitSchedulerImpl.java rename to api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java index b0aec6b..4d939cf 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/BukkitSchedulerImpl.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/CustomCropsReloadEvent.java @@ -15,36 +15,33 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.scheduler; +package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.CustomCrops; -import org.bukkit.Bukkit; -import org.bukkit.Location; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; +public class CustomCropsReloadEvent extends Event { -public class BukkitSchedulerImpl implements SchedulerPlatform { + private static final HandlerList handlerList = new HandlerList(); + private final CustomCropsPlugin plugin; - private final CustomCrops plugin; - - public BukkitSchedulerImpl(CustomCrops plugin) { + public CustomCropsReloadEvent(CustomCropsPlugin plugin) { this.plugin = plugin; } - @Override - public Future callSyncMethod(@NotNull Callable task) { - return Bukkit.getScheduler().callSyncMethod(plugin, task); + public static HandlerList getHandlerList() { + return handlerList; } + @NotNull @Override - public void runTask(Runnable runnable) { - Bukkit.getScheduler().runTask(plugin, runnable); + public HandlerList getHandlers() { + return getHandlerList(); } - @Override - public void runTask(Runnable runnable, Location location) { - runTask(runnable); + public CustomCropsPlugin getPluginInstance() { + return plugin; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java deleted file mode 100644 index a53a2fc..0000000 --- a/api/src/main/java/net/momirealms/customcrops/api/event/FertilizerUseEvent.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.event; - -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.PlayerEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -/** - * An event that triggered when player is using fertilizers - */ -public class FertilizerUseEvent extends PlayerEvent implements Cancellable { - - private static final HandlerList handlers = new HandlerList(); - private boolean cancelled; - private final ItemStack itemInHand; - private final String fertilizerKey; - private final Location location; - - public FertilizerUseEvent( - @NotNull Player who, - @NotNull ItemStack itemInHand, - @NotNull String fertilizerKey, - @NotNull Location location - ) { - super(who); - this.itemInHand = itemInHand; - this.fertilizerKey = fertilizerKey; - this.location = location; - } - - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public void setCancelled(boolean cancel) { - this.cancelled = cancel; - } - - /** - * Get the fertilizer item in hand - * @return fertilizer itemStack - */ - @NotNull - public ItemStack getItemInHand() { - return itemInHand; - } - - /** - * Get the fertilizer config key - * @return fertilizer key - */ - @NotNull - public String getFertilizerKey() { - return fertilizerKey; - } - - /** - * Get the pot location - * @return location - */ - @NotNull - public Location getLocation() { - return location; - } - - @NotNull - public static HandlerList getHandlerList() { - return handlers; - } - - @NotNull - @Override - public HandlerList getHandlers() { - return getHandlerList(); - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java index 4365834..8815520 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotBreakEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.event.Cancellable; @@ -33,17 +34,17 @@ public class PotBreakEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final String potKey; + private final WorldPot pot; private final Entity entity; public PotBreakEvent( @Nullable Entity entity, @NotNull Location location, - @NotNull String potKey + @NotNull WorldPot pot ) { this.entity = entity; this.location = location; - this.potKey = potKey; + this.pot = pot; } @Override @@ -76,13 +77,14 @@ public class PotBreakEvent extends Event implements Cancellable { return location; } + /** - * Get the pot config key - * @return pot key + * Get the pot's data + * @return pot */ @NotNull - public String getPotKey() { - return potKey; + public WorldPot getPot() { + return pot; } /** @@ -93,4 +95,4 @@ public class PotBreakEvent extends Event implements Cancellable { public Entity getEntity() { return entity; } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotInfoEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotInfoEvent.java deleted file mode 100644 index e29983e..0000000 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotInfoEvent.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.event; - -import net.momirealms.customcrops.api.object.CCFertilizer; -import net.momirealms.customcrops.api.object.CCGrowingCrop; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.PlayerEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * This event is called after a player interacted a pot - * So the fertilizer/water would be updated - */ -public class PotInfoEvent extends PlayerEvent { - - private static final HandlerList handlers = new HandlerList(); - private final CCFertilizer fertilizer; - private final int water; - private final CCGrowingCrop growingCrop; - private final ItemStack itemInHand; - private final Location location; - - - public PotInfoEvent( - @NotNull Player who, - @NotNull Location location, - @NotNull ItemStack itemInHand, - @Nullable CCFertilizer fertilizer, - int water, - @Nullable CCGrowingCrop growingCrop - ) { - super(who); - this.fertilizer = fertilizer; - this.water = water; - this.growingCrop = growingCrop; - this.itemInHand = itemInHand; - this.location = location; - } - - @NotNull - public static HandlerList getHandlerList() { - return handlers; - } - - @NotNull - @Override - public HandlerList getHandlers() { - return getHandlerList(); - } - - /** - * Get the fertilizer - * @return fertilizer - */ - @Nullable - public CCFertilizer getFertilizer() { - return fertilizer; - } - - /** - * Get the water amount - * @return water amount - */ - public int getWater() { - return water; - } - - /** - * Get the on growing crop above the pot - * It would be null if there's no crop or the crop is already ripe - * @return crop - */ - @Nullable - public CCGrowingCrop getGrowingCrop() { - return growingCrop; - } - - /** - * Get the item in player's hand - * If there's nothing in hand, it would return AIR - * @return item in hand - */ - @NotNull - public ItemStack getItemInHand() { - return itemInHand; - } - - /** - * Get the pot location - * @return location - */ - @NotNull - public Location getLocation() { - return location; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java index 24f36ed..52b947a 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotInteractEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -34,18 +35,18 @@ public class PotInteractEvent extends PlayerEvent implements Cancellable { private boolean cancelled; private final ItemStack itemInHand; private final Location location; - private final String potKey; + private final WorldPot pot; public PotInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @NotNull Location location, - @NotNull String potKey + @NotNull WorldPot pot ) { super(who); this.itemInHand = itemInHand; this.location = location; - this.potKey = potKey; + this.pot = pot; } @Override @@ -93,11 +94,11 @@ public class PotInteractEvent extends PlayerEvent implements Cancellable { } /** - * Get the pot's config key + * Get the pot's data * @return pot key */ @NotNull - public String getPotKey() { - return potKey; + public WorldPot getPot() { + return pot; } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java index 47712ce..875b1e3 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/PotPlaceEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.item.Pot; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -32,16 +33,16 @@ public class PotPlaceEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final String potKey; + private final Pot pot; public PotPlaceEvent( @NotNull Player who, @NotNull Location location, - @NotNull String potKey + @NotNull Pot pot ) { super(who); this.location = location; - this.potKey = potKey; + this.pot = pot; } @Override @@ -75,11 +76,11 @@ public class PotPlaceEvent extends PlayerEvent implements Cancellable { } /** - * Get the placed pot config key - * @return pot key + * Get the placed pot's config + * @return pot */ @NotNull - public String getPotKey() { - return potKey; + public Pot getPot() { + return pot; } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/PotWaterEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/PotWaterEvent.java deleted file mode 100644 index da6e191..0000000 --- a/api/src/main/java/net/momirealms/customcrops/api/event/PotWaterEvent.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.event; - -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.PlayerEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -/** - * An event that triggered when watering a pot - */ -public class PotWaterEvent extends PlayerEvent implements Cancellable { - - private static final HandlerList handlers = new HandlerList(); - private boolean cancelled; - private final ItemStack itemInHand; - private int water; - private final Location location; - - public PotWaterEvent( - @NotNull Player who, - @NotNull ItemStack itemInHand, - int water, - @NotNull Location location) { - super(who); - this.itemInHand = itemInHand; - this.water = water; - this.location = location; - } - - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public void setCancelled(boolean cancel) { - this.cancelled = cancel; - } - - @NotNull - public static HandlerList getHandlerList() { - return handlers; - } - - @NotNull - @Override - public HandlerList getHandlers() { - return getHandlerList(); - } - - /** - * Get the pot location - * @return location - */ - @NotNull - public Location getLocation() { - return location; - } - - /** - * Get the item in player's hand - * @return item in hand - */ - @NotNull - public ItemStack getItemInHand() { - return itemInHand; - } - - /** - * Get the amount of water - * @return the amount of water that added to the pot - */ - public int getWater() { - return water; - } - - /** - * Set the amount of water that added to the pot - * @param water water - */ - public void setWater(int water) { - this.water = water; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java index c421e1a..dfc4641 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SeasonChangeEvent.java @@ -17,25 +17,27 @@ package net.momirealms.customcrops.api.event; -import net.momirealms.customcrops.api.object.CCWorldSeason; +import net.momirealms.customcrops.api.mechanic.world.season.Season; import org.bukkit.World; +import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import org.bukkit.event.world.WorldEvent; import org.jetbrains.annotations.NotNull; /** - * An event that triggered when season changes + * An async event triggered when season changes */ -public class SeasonChangeEvent extends WorldEvent { +public class SeasonChangeEvent extends Event { private static final HandlerList handlers = new HandlerList(); - private final CCWorldSeason season; + private final Season season; + private final World world; public SeasonChangeEvent( @NotNull World world, - @NotNull CCWorldSeason season + @NotNull Season season ) { - super(world); + super(true); + this.world = world; this.season = season; } @@ -55,7 +57,15 @@ public class SeasonChangeEvent extends WorldEvent { * @return season */ @NotNull - public CCWorldSeason getSeason() { + public Season getSeason() { return season; } + + /** + * Get the world + * @return world + */ + public World getWorld() { + return world; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java index 36c3a89..a8bf779 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerBreakEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -32,16 +33,16 @@ public class SprinklerBreakEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final String sprinklerKey; + private final WorldSprinkler sprinkler; public SprinklerBreakEvent( @NotNull Player who, @NotNull Location location, - @NotNull String sprinklerKey + @NotNull WorldSprinkler sprinkler ) { super(who); this.location = location; - this.sprinklerKey = sprinklerKey; + this.sprinkler = sprinkler; } @Override @@ -75,11 +76,11 @@ public class SprinklerBreakEvent extends PlayerEvent implements Cancellable { } /** - * Get the sprinkler config key - * @return sprinkler key + * Get the sprinkler's data + * @return sprinkler */ @NotNull - public String getSprinklerKey() { - return sprinklerKey; + public WorldSprinkler getSprinkler() { + return sprinkler; } -} +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java deleted file mode 100644 index 0474ce3..0000000 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerFillEvent.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.event; - -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.HandlerList; -import org.bukkit.event.player.PlayerEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -/** - * An event that triggered when filling a sprinkler - */ -public class SprinklerFillEvent extends PlayerEvent implements Cancellable { - - private static final HandlerList handlers = new HandlerList(); - private boolean cancelled; - private final ItemStack itemInHand; - private int water; - private final Location location; - private final String sprinklerKey; - - public SprinklerFillEvent( - @NotNull Player who, - @NotNull String sprinklerKey, - @NotNull ItemStack itemInHand, - int water, - @NotNull Location location) { - super(who); - this.itemInHand = itemInHand; - this.water = water; - this.location = location; - this.sprinklerKey = sprinklerKey; - } - - @Override - public boolean isCancelled() { - return cancelled; - } - - @Override - public void setCancelled(boolean cancel) { - this.cancelled = cancel; - } - - @NotNull - public static HandlerList getHandlerList() { - return handlers; - } - - @NotNull - @Override - public HandlerList getHandlers() { - return getHandlerList(); - } - - /** - * Get the item in player's hand - * @return item in hand - */ - @NotNull - public ItemStack getItemInHand() { - return itemInHand; - } - - /** - * Get the sprinkler location - * @return location - */ - @NotNull - public Location getLocation() { - return location; - } - - /** - * Get the amount of water - * @return the amount of water that added to the sprinkler - */ - public int getWater() { - return water; - } - - /** - * Set the water that added to the sprinkler - * @param water water - */ - public void setWater(int water) { - this.water = water; - } - - /** - * Get the sprinkler config key - * @return sprinkler key - */ - @NotNull - public String getSprinklerKey() { - return sprinklerKey; - } -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java index 346a531..f8ae29d 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerInteractEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -33,18 +34,18 @@ public class SprinklerInteractEvent extends PlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final Location location; - private final String sprinklerKey; + private final WorldSprinkler sprinkler; private final ItemStack itemInHand; public SprinklerInteractEvent( @NotNull Player who, @NotNull ItemStack itemInHand, @NotNull Location location, - @NotNull String sprinklerKey + @NotNull WorldSprinkler sprinkler ) { super(who); this.location = location; - this.sprinklerKey = sprinklerKey; + this.sprinkler = sprinkler; this.itemInHand = itemInHand; } @@ -79,12 +80,12 @@ public class SprinklerInteractEvent extends PlayerEvent implements Cancellable { } /** - * Get the sprinkler config key - * @return sprinkler key + * Get the sprinkler's data + * @return sprinkler */ @NotNull - public String getSprinklerKey() { - return sprinklerKey; + public WorldSprinkler getSprinkler() { + return sprinkler; } /** diff --git a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java index b50d252..547a3f6 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java +++ b/api/src/main/java/net/momirealms/customcrops/api/event/SprinklerPlaceEvent.java @@ -17,6 +17,7 @@ package net.momirealms.customcrops.api.event; +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; @@ -34,13 +35,13 @@ public class SprinklerPlaceEvent extends PlayerEvent implements Cancellable { private boolean cancelled; private final ItemStack itemInHand; private final Location location; - private final String sprinklerKey; + private final Sprinkler sprinkler; - public SprinklerPlaceEvent(@NotNull Player who, ItemStack itemInHand, Location location, String sprinklerKey) { + public SprinklerPlaceEvent(@NotNull Player who, ItemStack itemInHand, Location location, Sprinkler sprinkler) { super(who); this.itemInHand = itemInHand; this.location = location; - this.sprinklerKey = sprinklerKey; + this.sprinkler = sprinkler; } @Override @@ -83,11 +84,11 @@ public class SprinklerPlaceEvent extends PlayerEvent implements Cancellable { } /** - * Get the sprinkler config key - * @return sprinkler key + * Get the sprinkler's config + * @return sprinkler */ @NotNull - public String getSprinklerKey() { - return sprinklerKey; + public Sprinkler getSprinkler() { + return sprinkler; } -} +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/ItemInterface.java b/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java similarity index 78% rename from plugin/src/main/java/net/momirealms/customcrops/integration/ItemInterface.java rename to api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java index 57ef3dd..0293d59 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/ItemInterface.java +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/ItemLibrary.java @@ -15,14 +15,16 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration; +package net.momirealms.customcrops.api.integration; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; -public interface ItemInterface { +public interface ItemLibrary { - @Nullable - ItemStack build(String id, Player player); + String identification(); + + ItemStack buildItem(Player player, String id); + + String getItemID(ItemStack itemStack); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/LevelInterface.java b/api/src/main/java/net/momirealms/customcrops/api/integration/LevelInterface.java new file mode 100644 index 0000000..b347613 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/LevelInterface.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.integration; + +import org.bukkit.entity.Player; + +public interface LevelInterface { + + /** + * Add exp to a certain skill or job + * + * @param player player + * @param target the skill or job, for instance "Fishing" "fisherman" + * @param amount the exp amount + */ + void addXp(Player player, String target, double amount); + + /** + * Get a player's skill or job's level + * + * @param player player + * @param target the skill or job, for instance "Fishing" "fisherman" + * @return level + */ + int getLevel(Player player, String target); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/integration/SeasonInterface.java b/api/src/main/java/net/momirealms/customcrops/api/integration/SeasonInterface.java new file mode 100644 index 0000000..10c4df5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/integration/SeasonInterface.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.integration; + +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import org.bukkit.World; +import org.jetbrains.annotations.Nullable; + +public interface SeasonInterface { + + /** + * Get a world's season + * + * @param world world + * @return spring, summer, autumn, winter or null + */ + @Nullable Season getSeason(World world); + + /** + * Get a world's date + * + * @param world world + * @return date + */ + int getDate(World world); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java new file mode 100644 index 0000000..2e94c26 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ActionManager.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionFactory; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import org.bukkit.configuration.ConfigurationSection; + +import java.util.HashMap; + +public interface ActionManager extends Reloadable { + + boolean registerAction(String type, ActionFactory actionFactory); + + boolean unregisterAction(String type); + + Action getAction(ConfigurationSection section); + + HashMap getActionMap(ConfigurationSection section); + + Action[] getActions(ConfigurationSection section); + + ActionFactory getActionFactory(String type); + + static void triggerActions(State state, Action... actions) { + if (actions != null) + for (Action action : actions) + action.trigger(state); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/AdventureManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/AdventureManager.java new file mode 100644 index 0000000..82348e4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/AdventureManager.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.common.Initable; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public abstract class AdventureManager implements Initable { + + private static AdventureManager instance; + + public AdventureManager() { + instance = this; + } + + public static AdventureManager getInstance() { + return instance; + } + + public abstract void sendMessage(CommandSender sender, String s); + + public abstract void sendMessageWithPrefix(CommandSender sender, String text); + + public abstract void sendConsoleMessage(String text); + + public abstract void sendPlayerMessage(Player player, String text); + + public abstract void sendActionbar(Player player, String text); + + public abstract void sendSound(Player player, Sound.Source source, Key key, float pitch, float volume); + + public abstract void sendSound(Player player, Sound sound); + + public abstract Component getComponentFromMiniMessage(String text); + + public abstract String legacyToMiniMessage(String legacy); + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public abstract boolean isColorCode(char c); + + public abstract void sendTitle(Player player, String title, String subTitle, int fadeIn, int stay, int fadeOut); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java new file mode 100644 index 0000000..adf3358 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ConditionManager.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.mechanic.condition.Condition; +import net.momirealms.customcrops.api.mechanic.condition.ConditionFactory; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ConditionManager extends Reloadable { + + boolean registerCondition(String type, ConditionFactory conditionFactory); + + boolean unregisterCondition(String type); + + boolean hasCondition(String type); + + @NotNull + Condition[] getConditions(ConfigurationSection section); + + Condition getCondition(ConfigurationSection section); + + Condition getCondition(String key, Object args); + + @Nullable ConditionFactory getConditionFactory(String type); + + static boolean isConditionMet(CustomCropsBlock block, Condition... conditions) { + if (conditions == null) return true; + for (Condition condition : conditions) { + if (!condition.isConditionMet(block)) { + return false; + } + } + return true; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java new file mode 100644 index 0000000..c730db4 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ConfigManager.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.common.Reloadable; +import org.bukkit.World; + +public abstract class ConfigManager implements Reloadable { + + private static ConfigManager instance; + + public ConfigManager() { + instance = this; + } + + public static ConfigManager getInstance() { + return instance; + } + + public static boolean legacyColorSupport() { + return instance.hasLegacyColorSupport(); + } + + public static int maximumPoolSize() { + return instance.getMaximumPoolSize(); + } + + public static int corePoolSize() { + return instance.getCorePoolSize(); + } + + public static int keepAliveTime() { + return instance.getKeepAliveTime(); + } + + public static boolean debug() { + return instance.getDebugMode(); + } + + public static boolean protectLore() { + return instance.isProtectLore(); + } + + public static String[] itemDetectionOrder() { + return instance.getItemDetectionOrder(); + } + + public static String lang() { + return instance.getLang(); + } + + public static boolean metrics() { + return instance.hasMetrics(); + } + + public static boolean checkUpdate() { + return instance.hasCheckUpdate(); + } + + public static double[] defaultQualityRatio() { + return instance.getDefaultQualityRatio(); + } + + public static boolean preventTrampling() { + return instance.isPreventTrampling(); + } + + public static boolean disableMoisture() { + return instance.isDisableMoisture(); + } + + public static boolean syncSeasons() { + return instance.isSyncSeasons(); + } + + public static boolean enableGreenhouse() { + return instance.isGreenhouseEnabled(); + } + + public static World referenceWorld() { + return instance.getReferenceWorld(); + } + + public static int greenhouseRange() { + return instance.getGreenhouseRange(); + } + + public static int scarecrowRange() { + return instance.getScarecrowRange(); + } + + public static String greenhouseID() { + return instance.getGreenhouseID(); + } + + public static boolean enableScarecrow() { + return instance.isScarecrowEnabled(); + } + + public static String scarecrowID() { + return instance.getScarecrowID(); + } + + protected abstract double[] getDefaultQualityRatio(); + + protected abstract String getLang(); + + protected abstract boolean getDebugMode(); + + protected abstract boolean hasLegacyColorSupport(); + + protected abstract int getMaximumPoolSize(); + + protected abstract int getKeepAliveTime(); + + protected abstract int getCorePoolSize(); + + public abstract boolean isProtectLore(); + + public abstract String[] getItemDetectionOrder(); + + public abstract boolean hasMetrics(); + + public abstract boolean hasCheckUpdate(); + + public abstract boolean isDisableMoisture(); + + public abstract boolean isPreventTrampling(); + + public abstract boolean isGreenhouseEnabled(); + + public abstract String getGreenhouseID(); + + public abstract int getGreenhouseRange(); + + public abstract boolean isScarecrowEnabled(); + + public abstract String getScarecrowID(); + + public abstract int getScarecrowRange(); + + public abstract boolean isSyncSeasons(); + + public abstract World getReferenceWorld(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/CoolDownManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/CoolDownManager.java new file mode 100644 index 0000000..92396f6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/CoolDownManager.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Reloadable; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class CoolDownManager implements Listener, Reloadable { + + private final ConcurrentHashMap dataMap; + private final CustomCropsPlugin plugin; + + public CoolDownManager(CustomCropsPlugin plugin) { + this.dataMap = new ConcurrentHashMap<>(); + this.plugin = plugin; + } + + public boolean isCoolDown(UUID uuid, String key, long time) { + Data data = this.dataMap.computeIfAbsent(uuid, k -> new Data()); + return data.isCoolDown(key, time); + } + + @Override + public void load() { + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + @Override + public void unload() { + HandlerList.unregisterAll(this); + } + + @Override + public void disable() { + unload(); + this.dataMap.clear(); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + dataMap.remove(event.getPlayer().getUniqueId()); + } + + public static class Data { + + private final HashMap coolDownMap; + + public Data() { + this.coolDownMap = new HashMap<>(); + } + + public synchronized boolean isCoolDown(String key, long delay) { + long time = System.currentTimeMillis(); + long last = coolDownMap.getOrDefault(key, time - delay); + if (last + delay > time) { + return true; + } else { + coolDownMap.put(key, time); + return false; + } + } + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java new file mode 100644 index 0000000..4f33ae5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/IntegrationManager.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.common.Initable; +import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.SeasonInterface; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +public interface IntegrationManager extends Initable { + + /** + * Registers a level plugin with the specified name. + * + * @param plugin The name of the level plugin. + * @param level The implementation of the LevelInterface. + * @return true if the registration was successful, false if the plugin name is already registered. + */ + boolean registerLevelPlugin(String plugin, LevelInterface level); + + /** + * Unregisters a level plugin with the specified name. + * + * @param plugin The name of the level plugin to unregister. + * @return true if the unregistration was successful, false if the plugin name is not found. + */ + boolean unregisterLevelPlugin(String plugin); + + /** + * Get the LevelInterface provided by a plugin. + * + * @param plugin The name of the plugin providing the LevelInterface. + * @return The LevelInterface provided by the specified plugin, or null if the plugin is not registered. + */ + @Nullable LevelInterface getLevelPlugin(String plugin); + + SeasonInterface getSeasonInterface(); + + Season getSeason(World world); + + int getDate(World world); + + ItemStack build(String itemID); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java new file mode 100644 index 0000000..95564aa --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/ItemManager.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.integration.ItemLibrary; +import net.momirealms.customcrops.api.mechanic.item.*; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public interface ItemManager extends Reloadable { + + boolean registerItemLibrary(@NotNull ItemLibrary itemLibrary); + + boolean unregisterItemLibrary(String identification); + + String getItemID(ItemStack itemStack); + + ItemStack getItemStack(Player player, String id); + + void placeItem(Location location, ItemCarrier carrier, String id); + + void removeAnythingAt(Location location); + + @Nullable + WateringCan getWateringCanByID(@NotNull String id); + + @Nullable + WateringCan getWateringCanByItemID(@NotNull String id); + + @Nullable + WateringCan getWateringCanByItemStack(@NotNull ItemStack itemStack); + + @Nullable + Sprinkler getSprinklerByID(@NotNull String id); + + @Nullable + Sprinkler getSprinklerBy3DItemID(@NotNull String id); + + @Nullable + Sprinkler getSprinklerBy2DItemID(@NotNull String id); + + @Nullable + Sprinkler getSprinklerByEntity(@NotNull Entity entity); + + @Nullable + Sprinkler getSprinklerBy2DItemStack(@NotNull ItemStack itemStack); + + @Nullable + Sprinkler getSprinklerBy3DItemStack(@NotNull ItemStack itemStack); + + @Nullable + Sprinkler getSprinklerByItemStack(@NotNull ItemStack itemStack); + + @Nullable + Pot getPotByID(@NotNull String id); + + @Nullable + Pot getPotByBlockID(@NotNull String id); + + @Nullable + Pot getPotByBlock(@NotNull Block block); + + @Nullable + Pot getPotByItemStack(@NotNull ItemStack itemStack); + + Fertilizer getFertilizerByID(String id); + + Fertilizer getFertilizerByItemID(String id); + + Fertilizer getFertilizerByItemStack(@NotNull ItemStack itemStack); + + Crop getCropByID(String id); + + Crop getCropBySeedID(String id); + + Crop getCropBySeedItemStack(ItemStack itemStack); + + Crop getCropByStageID(String id); + + Crop getCropByEntity(Entity entity); + + Crop getCropByBlock(Block block); + + Crop.Stage getCropStageByStageID(String id); + + void updatePotState(Location location, Pot pot, boolean hasWater, Fertilizer fertilizer); + + @NotNull + Collection getPotInRange(Location baseLocation, int width, int length, float yaw, String potID); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/MessageManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/MessageManager.java new file mode 100644 index 0000000..7fc653b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/MessageManager.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import org.jetbrains.annotations.Nullable; + +public abstract class MessageManager { + + private static MessageManager instance; + + public MessageManager() { + instance = this; + } + + public static MessageManager getInstance() { + return instance; + } + + public static String seasonTranslation(@Nullable Season season) { + return instance.getSeasonTranslation(season); + } + + public static String reloadMessage() { + return instance.getReload(); + } + + public static String prefix() { + return instance.getPrefix(); + } + + protected abstract String getPrefix(); + + protected abstract String getReload(); + + protected abstract String getSeasonTranslation(Season season); + + public abstract void reload(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/PlaceholderManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/PlaceholderManager.java new file mode 100644 index 0000000..a720b18 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/PlaceholderManager.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.common.Reloadable; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +public abstract class PlaceholderManager implements Reloadable { + + public static final Pattern pattern = Pattern.compile("\\{[^{}]+}"); + private static PlaceholderManager instance; + + public PlaceholderManager() { + instance = this; + } + + public static PlaceholderManager getInstance() { + return instance; + } + + public abstract String parse(Player player, String text, Map vars); + + public abstract List parse(Player player, List text, Map vars); + + public abstract List detectPlaceholders(String text); + + public abstract String setPlaceholders(Player player, String text); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java new file mode 100644 index 0000000..dc6eabe --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/RequirementManager.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.requirement.RequirementFactory; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface RequirementManager extends Reloadable { + + boolean registerRequirement(String type, RequirementFactory requirementFactory); + + boolean unregisterRequirement(String type); + + @Nullable + Requirement[] getRequirements(ConfigurationSection section, boolean advanced); + + boolean hasRequirement(String type); + + @NotNull + Requirement getRequirement(ConfigurationSection section, boolean advanced); + + @NotNull + Requirement getRequirement(String type, Object value); + + @Nullable + RequirementFactory getRequirementFactory(String type); + + static boolean isRequirementMet(State condition, Requirement... requirements) { + if (requirements == null) return true; + for (Requirement requirement : requirements) { + if (!requirement.isStateMet(condition)) { + return false; + } + } + return true; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java new file mode 100644 index 0000000..9aa6a66 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/VersionManager.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import java.util.concurrent.CompletionStage; + +public abstract class VersionManager { + + private static VersionManager instance; + + public VersionManager() { + instance = this; + } + + public static VersionManager getInstance() { + return instance; + } + + public static boolean isHigherThan1_19_R3() { + return instance.isVersionNewerThan1_19_R3(); + } + + public static boolean isHigherThan1_19_R2() { + return instance.isVersionNewerThan1_19_R2(); + } + + public abstract boolean hasRegionScheduler(); + + public static boolean folia() { + return instance.hasRegionScheduler(); + } + + public abstract String getPluginVersion(); + + public static String pluginVersion() { + return instance.getPluginVersion(); + } + + public static String serverVersion() { + return instance.getServerVersion(); + } + + public abstract String getServerVersion(); + + public static boolean spigot() { + return instance.isSpigot(); + } + + public abstract boolean isSpigot(); + + public abstract boolean isVersionNewerThan1_19_R3(); + + public abstract boolean isVersionNewerThan1_19(); + + public abstract boolean isVersionNewerThan1_19_R2(); + + public abstract boolean isVersionNewerThan1_20(); + + public abstract boolean isMojmap(); + + public abstract CompletionStage checkUpdate(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java new file mode 100644 index 0000000..d354fea --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/manager/WorldManager.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.manager; + +import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.mechanic.item.*; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.*; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Optional; + +public interface WorldManager extends Reloadable { + + /** + * Load a specified world and convert it into a CustomCrops world + * This method ignores the whitelist and blacklist + * If there already exists one, it would not create a new instance but return the created one + * + * @param world world + */ + @NotNull + CustomCropsWorld loadWorld(@NotNull World world); + + /** + * Unload a specified world and save it to file + * This method ignores the whitelist and blacklist + * + * @param world world + */ + boolean unloadWorld(@NotNull World world); + + /** + * Check if the world has CustomCrops mechanisms + * + * @param world world + * @return has or not + */ + boolean isMechanicEnabled(@NotNull World world); + + /** + * Get all the worlds loaded in CustomCrops + * + * @return worlds + */ + @NotNull + Collection getWorldNames(); + + /** + * Get all the worlds loaded in CustomCrops + * + * @return worlds + */ + @NotNull + Collection getBukkitWorlds(); + + /** + * Get all the worlds loaded in CustomCrops + * + * @return worlds + */ + @NotNull + Collection getCustomCropsWorlds(); + + @NotNull + Optional getCustomCropsWorld(@NotNull String name); + + @NotNull + Optional getCustomCropsWorld(@NotNull World world); + + @NotNull + Optional getSprinklerAt(@NotNull SimpleLocation location); + + @NotNull + Optional getPotAt(@NotNull SimpleLocation location); + + @NotNull + Optional getCropAt(@NotNull SimpleLocation location); + + @NotNull Optional getGlassAt(@NotNull SimpleLocation location); + + @NotNull Optional getScarecrowAt(@NotNull SimpleLocation location); + + Optional getBlockAt(SimpleLocation location); + + void addWaterToSprinkler(@NotNull Sprinkler sprinkler, @NotNull SimpleLocation location, int amount); + + void addFertilizerToPot(@NotNull Pot pot, @NotNull Fertilizer fertilizer, @NotNull SimpleLocation location); + + void addWaterToPot(@NotNull Pot pot, @NotNull SimpleLocation location, int amount); + + void addGlassAt(@NotNull WorldGlass glass, @NotNull SimpleLocation location); + + void addScarecrowAt(@NotNull WorldScarecrow scarecrow, @NotNull SimpleLocation location); + + void removeSprinklerAt(@NotNull SimpleLocation location); + + void removePotAt(@NotNull SimpleLocation location); + + void removeCropAt(@NotNull SimpleLocation location); + + boolean isReachLimit(SimpleLocation location, ItemType itemType); + + void addPotAt(@NotNull WorldPot pot, @NotNull SimpleLocation location); + + void addSprinklerAt(@NotNull WorldSprinkler sprinkler, @NotNull SimpleLocation location); + + void addCropAt(@NotNull WorldCrop crop, @NotNull SimpleLocation location); + + void addPointToCrop(@NotNull Crop crop, @NotNull SimpleLocation location, int points); + + void handleChunkLoad(Chunk bukkitChunk); + + void handleChunkUnload(Chunk bukkitChunk); + + void saveChunkToFile(CustomCropsChunk chunk); + + void removeGlassAt(@NotNull SimpleLocation location); + + void removeScarecrowAt(@NotNull SimpleLocation location); + + CustomCropsBlock removeAnythingAt(SimpleLocation location); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java new file mode 100644 index 0000000..860b7b5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/Action.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.action; + +import net.momirealms.customcrops.api.mechanic.requirement.State; + +public interface Action { + + void trigger(State condition); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java new file mode 100644 index 0000000..e349c7c --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionExpansion.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.action; + +public abstract class ActionExpansion { + + public abstract String getVersion(); + + public abstract String getAuthor(); + + public abstract String getActionType(); + + public abstract ActionFactory getActionFactory(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java new file mode 100644 index 0000000..61fa136 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionFactory.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.action; + +public interface ActionFactory { + + Action build(Object args, double chance); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionTrigger.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionTrigger.java new file mode 100644 index 0000000..4ae87a6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/action/ActionTrigger.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.action; + +public enum ActionTrigger { + + BREAK, + PLACE, + GROW, + ADD_WATER, + NO_WATER, + CONSUME_WATER, + FULL, + WORK, + USE, + WRONG_POT, + WRONG_SPRINKLER, + BEFORE_PLANT, + REACH_LIMIT, + INTERACT, PLANT, RIPE, +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Condition.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java similarity index 79% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Condition.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java index 80ad9ab..8b7f1e8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Condition.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Condition.java @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.condition; +package net.momirealms.customcrops.api.mechanic.condition; -import net.momirealms.customcrops.api.object.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; public interface Condition { - boolean isMet(SimpleLocation simpleLocation); + boolean isConditionMet(CustomCropsBlock block); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java new file mode 100644 index 0000000..1f8691c --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionExpansion.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.condition; + +public abstract class ConditionExpansion { + + public abstract String getVersion(); + + public abstract String getAuthor(); + + public abstract String getConditionType(); + + public abstract ConditionFactory getConditionFactory(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java new file mode 100644 index 0000000..bde4c2a --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/ConditionFactory.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.condition; + +public interface ConditionFactory { + + Condition build(Object args); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java new file mode 100644 index 0000000..e0f7c9a --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/Conditions.java @@ -0,0 +1,14 @@ +package net.momirealms.customcrops.api.mechanic.condition; + +public class Conditions { + + private final Condition[] conditions; + + public Conditions(Condition[] conditions) { + this.conditions = conditions; + } + + public Condition[] getConditions() { + return conditions; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java new file mode 100644 index 0000000..501d2c0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/condition/DeathConditions.java @@ -0,0 +1,29 @@ +package net.momirealms.customcrops.api.mechanic.condition; + +import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; + +public class DeathConditions extends Conditions { + + private final String deathItem; + private final ItemCarrier itemCarrier; + private final int deathDelay; + + public DeathConditions(Condition[] conditions, String deathItem, ItemCarrier itemCarrier, int deathDelay) { + super(conditions); + this.deathItem = deathItem; + this.itemCarrier = itemCarrier; + this.deathDelay = deathDelay; + } + + public String getDeathItem() { + return deathItem; + } + + public ItemCarrier getItemCarrier() { + return itemCarrier; + } + + public int getDeathDelay() { + return deathDelay; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java new file mode 100644 index 0000000..2a988c2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/BoneMeal.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item; + +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.manager.ActionManager; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.requirement.State; + +import java.util.List; + +public class BoneMeal { + + private final String item; + private final int usedAmount; + private final String returned; + private final int returnedAmount; + private final List> pointGainList; + private final Action[] actions; + + public BoneMeal(String item, int usedAmount, String returned, int returnedAmount, List> pointGainList, Action[] actions) { + this.item = item; + this.returned = returned; + this.pointGainList = pointGainList; + this.actions = actions; + this.usedAmount = usedAmount; + this.returnedAmount = returnedAmount; + } + + public String getItem() { + return item; + } + + public String getReturned() { + return returned; + } + + public int getPoint() { + for (Pair pair : pointGainList) { + if (Math.random() < pair.left()) { + return pair.right(); + } + } + return 0; + } + + public void trigger(State state) { + ActionManager.triggerActions(state, actions); + } + + public int getUsedAmount() { + return usedAmount; + } + + public int getReturnedAmount() { + return returnedAmount; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java new file mode 100644 index 0000000..6b846fb --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Crop.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item; + +import net.momirealms.customcrops.api.common.item.KeyItem; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.condition.Conditions; +import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.HashSet; + +public interface Crop extends KeyItem { + + String getSeedItemID(); + + int getMaxPoints(); + + Requirement[] getPlantRequirements(); + + Requirement[] getBreakRequirements(); + + Requirement[] getInteractRequirements(); + + Conditions getGrowConditions(); + + DeathConditions[] getDeathConditions(); + + BoneMeal[] getBoneMeals(); + + boolean isRotation(); + + void trigger(ActionTrigger trigger, State state); + + Stage getStageByPoint(int point); + + String getStageItemByPoint(int point); + + Stage getStageByItemID(String itemID); + + Collection getStages(); + + HashSet getPotWhitelist(); + + ItemCarrier getItemCarrier(); + + interface Stage { + + double getHologramOffset(); + + @Nullable String getStageID(); + + int getPoint(); + + void trigger(ActionTrigger trigger, State state); + + Requirement[] getInteractRequirements(); + + Requirement[] getBreakRequirements(); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/object/CCGrowingCrop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java similarity index 59% rename from api/src/main/java/net/momirealms/customcrops/api/object/CCGrowingCrop.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java index 2f43ebd..3e44779 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/object/CCGrowingCrop.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Fertilizer.java @@ -15,28 +15,27 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.mechanic.item; -/** - * Crops - */ -public interface CCGrowingCrop { +import net.momirealms.customcrops.api.common.item.EventItem; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; - /** - * Get the crop growing point - * @return point - */ - int getPoints(); +import java.util.HashSet; - /** - * Set the growing point - * @param points points - */ - void setPoints(int points); - - /** - * Get the crop config key - * @return crop key - */ +public interface Fertilizer extends EventItem { String getKey(); + + String getItemID(); + + int getTimes(); + + FertilizerType getFertilizerType(); + + HashSet getPotWhitelist(); + + boolean isBeforePlant(); + + String getIcon(); + + Requirement[] getRequirements(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerType.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/FertilizerType.java similarity index 93% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerType.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/FertilizerType.java index f83d472..2e6898c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerType.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/FertilizerType.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.fertilizer; +package net.momirealms.customcrops.api.mechanic.item; public enum FertilizerType { SPEED_GROW, diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemCarrier.java similarity index 81% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemCarrier.java index 2f29bdd..449d710 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/ItemMode.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemCarrier.java @@ -15,16 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.mechanic.item; -import java.io.Serializable; - -public enum ItemMode implements Serializable { - - ARMOR_STAND, +public enum ItemCarrier { + NOTE_BLOCK, + MUSHROOM, + CHORUS, TRIPWIRE, ITEM_FRAME, - ITEM_DISPLAY, - NOTE_BLOCK, - CHORUS + ITEM_DISPLAY } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/ItemType.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemType.java similarity index 87% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/ItemType.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemType.java index a0e1d65..b28b3e8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/ItemType.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/ItemType.java @@ -15,15 +15,12 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.mechanic.item; public enum ItemType { - - GLASS, - POT, CROP, + POT, SPRINKLER, - SCARECROW, - WATERING_CAN, - UNKNOWN + GREENHOUSE, + SCARECROW } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java new file mode 100644 index 0000000..3a865ed --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Pot.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item; + +import net.momirealms.customcrops.api.common.item.KeyItem; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; +import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.requirement.State; + +import java.util.HashSet; + +public interface Pot extends KeyItem { + + int getStorage(); + + String getKey(); + + HashSet getPotBlocks(); + + PassiveFillMethod[] getPassiveFillMethods(); + + String getDryItem(); + + String getWetItem(); + + Requirement[] getPlaceRequirements(); + + Requirement[] getBreakRequirements(); + + Requirement[] getUseRequirements(); + + void trigger(ActionTrigger trigger, State state); + + WaterBar getWaterBar(); + + boolean isRainDropAccepted(); + boolean isNearbyWaterAccepted(); + + String getBlockState(boolean water, FertilizerType type); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java new file mode 100644 index 0000000..cc4d33f --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Scarecrow.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item; + +import net.momirealms.customcrops.api.common.item.KeyItem; + +public interface Scarecrow extends KeyItem { + + String getItemID(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java new file mode 100644 index 0000000..addf6d5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/Sprinkler.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item; + +import net.momirealms.customcrops.api.common.item.KeyItem; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; +import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.requirement.State; + +import java.util.HashSet; + +public interface Sprinkler extends KeyItem { + + String get2DItemID(); + + String get3DItemID(); + + String get3DItemWithWater(); + + int getStorage(); + + int getRange(); + + boolean isInfinite(); + + int getWater(); + + HashSet getPotWhitelist(); + + ItemCarrier getItemCarrier(); + + PassiveFillMethod[] getPassiveFillMethods(); + + Requirement[] getPlaceRequirements(); + + Requirement[] getBreakRequirements(); + + Requirement[] getUseRequirements(); + + void trigger(ActionTrigger trigger, State state); + + WaterBar getWaterBar(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java new file mode 100644 index 0000000..645eda5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/WateringCan.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item; + +import net.momirealms.customcrops.api.common.item.KeyItem; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +public interface WateringCan extends KeyItem { + + String getItemID(); + + int getWidth(); + + int getLength(); + + int getStorage(); + + int getWater(); + + boolean hasDynamicLore(); + + void updateItem(Player player, ItemStack itemStack, int water, Map args); + + int getCurrentWater(ItemStack itemStack); + + HashSet getPotWhitelist(); + + HashSet getSprinklerWhitelist(); + + List getLore(); + + @Nullable WaterBar getWaterBar(); + + Requirement[] getRequirements(); + + boolean isInfinite(); + + void trigger(ActionTrigger trigger, State state); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/SeasonInterface.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java similarity index 75% rename from plugin/src/main/java/net/momirealms/customcrops/integration/SeasonInterface.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java index d066934..d50b9b0 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/SeasonInterface.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/QualityCrop.java @@ -15,13 +15,12 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration; +package net.momirealms.customcrops.api.mechanic.item.fertilizer; -import net.momirealms.customcrops.api.object.season.CCSeason; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; -public interface SeasonInterface { +public interface QualityCrop extends Fertilizer { + double getChance(); - CCSeason getSeason(String world); - - int getDate(String world); + double[] getRatio(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/Function.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java similarity index 77% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/Function.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java index 386b32c..aa01a20 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/Function.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SoilRetain.java @@ -15,23 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.mechanic.item.fertilizer; -public class Function { +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; - public void init() { +public interface SoilRetain extends Fertilizer { - } - - public void load() { - - } - - public void unload() { - - } - - public void disable() { - - } + double getChance(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java new file mode 100644 index 0000000..23c0ec2 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/SpeedGrow.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item.fertilizer; + +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; + +public interface SpeedGrow extends Fertilizer { + + int getPointBonus(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java new file mode 100644 index 0000000..727c62d --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/Variation.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item.fertilizer; + +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; + +public interface Variation extends Fertilizer { + + double getChanceBonus(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java new file mode 100644 index 0000000..cae5804 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/fertilizer/YieldIncrease.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.item.fertilizer; + +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; + +public interface YieldIncrease extends Fertilizer { + + int getAmountBonus(); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fill/AbstractFillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/AbstractFillMethod.java similarity index 50% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/fill/AbstractFillMethod.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/AbstractFillMethod.java index f48b8ef..381a3c6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fill/AbstractFillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/AbstractFillMethod.java @@ -15,35 +15,35 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.fill; +package net.momirealms.customcrops.api.mechanic.item.water; -import net.kyori.adventure.sound.Sound; -import org.bukkit.Particle; -import org.jetbrains.annotations.Nullable; +import net.momirealms.customcrops.api.manager.ActionManager; +import net.momirealms.customcrops.api.manager.RequirementManager; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.requirement.State; public abstract class AbstractFillMethod { protected int amount; - protected Particle particle; - protected Sound sound; + private final Action[] actions; + private final Requirement[] requirements; - protected AbstractFillMethod(int amount, Particle particle, Sound sound) { + protected AbstractFillMethod(int amount, Action[] actions, Requirement[] requirements) { this.amount = amount; - this.particle = particle; - this.sound = sound; + this.actions = actions; + this.requirements = requirements; } public int getAmount() { return amount; } - @Nullable - public Particle getParticle() { - return particle; + public void trigger(State state) { + ActionManager.triggerActions(state, actions); } - - public Sound getSound() { - return sound; + public boolean canFill(State state) { + return RequirementManager.isRequirementMet(state, requirements); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fill/PassiveFillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java similarity index 53% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/fill/PassiveFillMethod.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java index 5a636aa..54f4c44 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fill/PassiveFillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PassiveFillMethod.java @@ -15,32 +15,40 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.fill; +package net.momirealms.customcrops.api.mechanic.item.water; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.CustomCrops; -import org.bukkit.Particle; -import org.bukkit.inventory.ItemStack; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; import org.jetbrains.annotations.Nullable; public class PassiveFillMethod extends AbstractFillMethod { private final String used; + private final int usedAmount; private final String returned; + private final int returnedAmount; - public PassiveFillMethod(String used, @Nullable String returned, int amount, @Nullable Particle particle, @Nullable Sound sound) { - super(amount, particle, sound); + public PassiveFillMethod(String used, int usedAmount, @Nullable String returned, int returnedAmount, int amount, Action[] actions, Requirement[] requirements) { + super(amount, actions, requirements); this.used = used; this.returned = returned; + this.usedAmount = usedAmount; + this.returnedAmount = returnedAmount; } - public boolean isRightItem(String item_id) { - return used.equals(item_id); + public String getUsed() { + return used; } - @Nullable - public ItemStack getReturnedItemStack() { - if (returned == null) return null; - return CustomCrops.getInstance().getIntegrationManager().build(returned); + public String getReturned() { + return returned; + } + + public int getUsedAmount() { + return usedAmount; + } + + public int getReturnedAmount() { + return returnedAmount; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fill/PositiveFillMethod.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java similarity index 71% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/fill/PositiveFillMethod.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java index 551d263..8d91b8f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fill/PositiveFillMethod.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/item/water/PositiveFillMethod.java @@ -15,17 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.fill; +package net.momirealms.customcrops.api.mechanic.item.water; -import net.kyori.adventure.sound.Sound; -import org.bukkit.Particle; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; public class PositiveFillMethod extends AbstractFillMethod { private final String id; - public PositiveFillMethod(String id, int amount, Particle particle, Sound sound) { - super(amount, particle, sound); + public PositiveFillMethod(String id, int amount, Action[] actions, Requirement[] requirements) { + super(amount, actions, requirements); this.id = id; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiRequirement.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java similarity index 84% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiRequirement.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java index 1aa9df0..503ba16 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiRequirement.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/Value.java @@ -15,10 +15,11 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.requirement.papi; +package net.momirealms.customcrops.api.mechanic.misc; import org.bukkit.entity.Player; -public interface PapiRequirement { - boolean isMet(Player player); +public interface Value { + + double get(Player player); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/image/WaterBar.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/image/WaterBar.java new file mode 100644 index 0000000..0cbe359 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/misc/image/WaterBar.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.misc.image; + +public record WaterBar(String left, String empty, String full, String right) { + + public static WaterBar of(String left, String empty, String full, String right) { + return new WaterBar(left, empty, full, right); + } + + public String getWaterBar(int current, int max) { + return left + + String.valueOf(full).repeat(current) + + String.valueOf(empty).repeat(Math.max(max - current, 0)) + + right; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/Requirement.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java similarity index 86% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/Requirement.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java index a2d177f..e708478 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/Requirement.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/Requirement.java @@ -15,9 +15,9 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.requirement; +package net.momirealms.customcrops.api.mechanic.requirement; public interface Requirement { - boolean isConditionMet(CurrentState currentState); + boolean isStateMet(State state); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java new file mode 100644 index 0000000..9f9972f --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementExpansion.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.requirement; + +public abstract class RequirementExpansion { + + public abstract String getVersion(); + + public abstract String getAuthor(); + + public abstract String getRequirementType(); + + public abstract RequirementFactory getRequirementFactory(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java new file mode 100644 index 0000000..cf429ec --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/RequirementFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.requirement; + +import net.momirealms.customcrops.api.mechanic.action.Action; + +import java.util.List; + +public interface RequirementFactory { + + Requirement build(Object args, List notMetActions, boolean advanced); + + default Requirement build(Object args) { + return build(args, null, false); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java new file mode 100644 index 0000000..cbb0dd6 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/requirement/State.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.requirement; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public class State { + + private final Player player; + private final ItemStack itemInHand; + private final Location location; + private final HashMap args; + + public State(Player player, ItemStack itemInHand, @NotNull Location location) { + this.player = player; + this.itemInHand = itemInHand; + this.location = location.toBlockLocation(); + this.args = new HashMap<>(); + if (player != null) { + setArg("{player}", player.getName()); + } + setArg("{x}", String.valueOf(location.getBlockX())); + setArg("{y}", String.valueOf(location.getBlockY())); + setArg("{z}", String.valueOf(location.getBlockZ())); + } + + public Player getPlayer() { + return player; + } + + public ItemStack getItemInHand() { + return itemInHand; + } + + public Location getLocation() { + return location; + } + + public Map getArgs() { + return args; + } + + public void setArg(String key, String value) { + args.put(key, value); + } + + public String getArg(String key) { + return args.get(key); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/ChunkCoordinate.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkCoordinate.java similarity index 63% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/world/ChunkCoordinate.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkCoordinate.java index c6295a5..81e93c6 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/ChunkCoordinate.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkCoordinate.java @@ -15,34 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.world; +package net.momirealms.customcrops.api.mechanic.world; import org.bukkit.Chunk; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.io.Serializable; +public record ChunkCoordinate(int x, int z) { -public class ChunkCoordinate implements Serializable { + private static final ChunkCoordinate empty = new ChunkCoordinate(0, 0); - private final int x; - private final int z; - - public ChunkCoordinate(int x, int z) { - this.x = x; - this.z = z; - } - - public int getX() { - return x; - } - - public int getZ() { - return z; - } - - public String getFileName() { - return x + "," + z; + public static ChunkCoordinate of(int x, int z) { + return new ChunkCoordinate(x, z); } @Override @@ -69,21 +52,16 @@ public class ChunkCoordinate implements Serializable { return true; } - @Nullable - public static ChunkCoordinate getByString(@NotNull String str) { - String[] split = str.split(",", 2); - try { - int x = Integer.parseInt(split[0]); - int z = Integer.parseInt(split[1]); - return new ChunkCoordinate(x, z); - } - catch (NumberFormatException e) { - return null; - } - } - @NotNull public static ChunkCoordinate getByBukkitChunk(@NotNull Chunk chunk) { return new ChunkCoordinate(chunk.getX(), chunk.getZ()); } + + @Override + public String toString() { + return "ChunkCoordinate{" + + "x=" + x + + ", z=" + z + + '}'; + } } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkPos.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkPos.java new file mode 100644 index 0000000..aca71ca --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/ChunkPos.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world; + +import java.util.Objects; + +public class ChunkPos { + + private final int position; + + public ChunkPos(int position) { + this.position = position; + } + + public ChunkPos(int x, int y, int z) { + this.position = ((x & 0xF) << 28) | ((z & 0xF) << 24) | (y & 0xFFFFFF); + } + + public static ChunkPos getByLocation(SimpleLocation location) { + return new ChunkPos(location.getX() % 16, location.getY(), location.getZ() % 16); + } + + public SimpleLocation getLocation(String world, ChunkCoordinate coordinate) { + return new SimpleLocation(world, coordinate.x() * 16 + getX(), getY(), coordinate.z() * 16 + getZ()); + } + + public int getPosition() { + return position; + } + + public int getX() { + return (position >> 28) & 0xF; + } + + public int getZ() { + return (position >> 24) & 0xF; + } + + public int getSectionID() { + return getY() / 16; + } + + public int getY() { + int y = position & 0xFFFFFF; + if ((y & 0x800000) != 0) { + y |= 0xFF000000; + } + return y; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChunkPos chunkPos = (ChunkPos) o; + return position == chunkPos.position; + } + + @Override + public int hashCode() { + return Objects.hash(position); + } + + @Override + public String toString() { + return "ChunkPos{" + + "x=" + getX() + + "y=" + getY() + + "z=" + getZ() + + '}'; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java new file mode 100644 index 0000000..2f2fa6f --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/CustomCropsBlock.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world; + +import net.momirealms.customcrops.api.mechanic.item.ItemType; +import net.momirealms.customcrops.api.mechanic.world.level.DataBlock; + +public interface CustomCropsBlock extends DataBlock, Tickable { + + ItemType getType(); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/SimpleLocation.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java similarity index 70% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/world/SimpleLocation.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java index 7fd959b..a83ae58 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/SimpleLocation.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SimpleLocation.java @@ -15,26 +15,24 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.world; +package net.momirealms.customcrops.api.mechanic.world; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.jetbrains.annotations.Nullable; -import java.io.Serial; -import java.io.Serializable; import java.util.Objects; -public class SimpleLocation implements Serializable { +public class SimpleLocation { - @Serial - private static final long serialVersionUID = -1288860694388882412L; + private int x; + private int y; + private int z; + private String worldName; - private final int x; - private final int y; - private final int z; - private final String worldName; + private SimpleLocation() { + } public SimpleLocation(String worldName, int x, int y, int z){ this.worldName = worldName; @@ -64,7 +62,10 @@ public class SimpleLocation implements Serializable { } public SimpleLocation add(int x, int y, int z) { - return new SimpleLocation(worldName, this.x + x, this.y + y, this.z + z); + this.x += x; + this.y += y; + this.z += z; + return this; } @Override @@ -79,13 +80,13 @@ public class SimpleLocation implements Serializable { if (!Objects.equals(worldName, other.getWorldName())) { return false; } - if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) { + if (this.x != other.x) { return false; } - if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) { + if (this.y != other.y) { return false; } - if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z)) { + if (this.z != other.z) { return false; } return true; @@ -94,9 +95,8 @@ public class SimpleLocation implements Serializable { @Override public int hashCode() { int hash = 3; - //hash = 19 * hash + (worldName != null ? worldName.hashCode() : 0); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); - hash = 19 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); + hash = 7 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); + hash = 13 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32)); return hash; } @@ -113,18 +113,23 @@ public class SimpleLocation implements Serializable { return Bukkit.getWorld(worldName); } - public static SimpleLocation getByString(String location, String world) { - String[] loc = location.split(","); - return new SimpleLocation(world, Integer.parseInt(loc[0]), Integer.parseInt(loc[1]), Integer.parseInt(loc[2])); + public static SimpleLocation getByString(String location) { + String[] loc = location.split(",", 4); + return new SimpleLocation(loc[0], Integer.parseInt(loc[1]), Integer.parseInt(loc[2]), Integer.parseInt(loc[3])); } - public static SimpleLocation getByBukkitLocation(Location location) { + public static SimpleLocation of(Location location) { return new SimpleLocation(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()); } @Override public String toString() { - return "[" + worldName + "," + x + "," + y + "," + z + "]"; + return "SimpleLocation{" + + "x=" + x + + ", y=" + y + + ", z=" + z + + ", worldName='" + worldName + '\'' + + '}'; } public boolean isNear(SimpleLocation simpleLocation, int distance) { diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java new file mode 100644 index 0000000..c6f24eb --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/SynchronizedCompoundMap.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.Tag; + +import java.util.Objects; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class SynchronizedCompoundMap { + + private final CompoundMap compoundMap; + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); + private final java.util.concurrent.locks.Lock readLock = rwLock.readLock(); + private final java.util.concurrent.locks.Lock writeLock = rwLock.writeLock(); + + public SynchronizedCompoundMap(CompoundMap compoundMap) { + this.compoundMap = compoundMap; + } + + public CompoundMap getOriginalMap() { + return compoundMap; + } + + public Tag get(String key) { + readLock.lock(); + try { + return compoundMap.get(key); + } finally { + readLock.unlock(); + } + } + + public Tag put(String key, Tag tag) { + writeLock.lock(); + try { + return compoundMap.put(key, tag); + } finally { + writeLock.unlock(); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SynchronizedCompoundMap that = (SynchronizedCompoundMap) o; + return Objects.equals(compoundMap, that.compoundMap); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java new file mode 100644 index 0000000..047517b --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/Tickable.java @@ -0,0 +1,6 @@ +package net.momirealms.customcrops.api.mechanic.world; + +public interface Tickable { + + void tick(int interval); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java new file mode 100644 index 0000000..23ad475 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/AbstractCustomCropsBlock.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.IntTag; +import com.flowpowered.nbt.Tag; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.SynchronizedCompoundMap; + +public class AbstractCustomCropsBlock implements DataBlock { + + private final SimpleLocation location; + private final SynchronizedCompoundMap compoundMap; + + public AbstractCustomCropsBlock(SimpleLocation location, CompoundMap compoundMap) { + this.compoundMap = new SynchronizedCompoundMap(compoundMap); + this.location = location; + } + + @Override + public void setData(String key, Tag tag) { + compoundMap.put(key, tag); + } + + @Override + public Tag getData(String name) { + return compoundMap.get(name); + } + + @Override + public SynchronizedCompoundMap getCompoundMap() { + return compoundMap; + } + + @Override + public SimpleLocation getLocation() { + return location; + } + + public boolean canTick(int interval) { + if (interval == 1) { + return true; + } + Tag tag = getData("tick"); + int tick = 0; + if (tag != null) { + tick = tag.getAsIntTag().map(IntTag::getValue).orElse(0); + } + if (++tick >= interval) { + setData("tick", new IntTag("tick", 0)); + return true; + } else { + setData("tick", new IntTag("tick", tick)); + } + return false; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java new file mode 100644 index 0000000..c3062fe --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsChunk.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; + +import java.util.Optional; + +public interface CustomCropsChunk { + + void notifyOfflineUpdates(); + + CustomCropsWorld getCustomCropsWorld(); + + ChunkCoordinate getChunkCoordinate(); + + void secondTimer(); + + long getLastLoadedTime(); + + int getLoadedSeconds(); + + Optional getCropAt(SimpleLocation location); + + Optional getSprinklerAt(SimpleLocation location); + + Optional getPotAt(SimpleLocation location); + + Optional getGlassAt(SimpleLocation location); + + Optional getScarecrowAt(SimpleLocation location); + + Optional getBlockAt(SimpleLocation location); + + void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount); + + void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location); + + void addWaterToPot(Pot pot, SimpleLocation location, int amount); + + void removeSprinklerAt(SimpleLocation location); + + void removePotAt(SimpleLocation location); + + void removeCropAt(SimpleLocation location); + + void removeGlassAt(SimpleLocation location); + + void removeScarecrowAt(SimpleLocation location); + + CustomCropsBlock removeBlockAt(SimpleLocation location); + + CustomCropsBlock addBlockAt(CustomCropsBlock block, SimpleLocation location); + + int getCropAmount(); + + int getPotAmount(); + + int getSprinklerAmount(); + + void addPotAt(WorldPot pot, SimpleLocation location); + + void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location); + + void addCropAt(WorldCrop crop, SimpleLocation location); + + void addPointToCrop(Crop crop, SimpleLocation location, int points); + + void addGlassAt(WorldGlass glass, SimpleLocation location); + + void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location); + + void updateLastLoadedTime(); + + CustomCropsSection[] getSections(); + + CustomCropsSection getSection(int sectionID); + + int getUnloadedSeconds(); + + void setUnloadedSeconds(int unloadedSeconds); + + boolean canPrune(); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java index 534c311..c0b2f3f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/RotationUtils.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsSection.java @@ -15,22 +15,26 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.util; +package net.momirealms.customcrops.api.mechanic.world.level; -import org.bukkit.Rotation; +import net.momirealms.customcrops.api.mechanic.world.ChunkPos; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import java.util.Random; +import java.util.Map; -public class RotationUtils { +public interface CustomCropsSection { - private static final Rotation[] rotations4 = {Rotation.NONE, Rotation.FLIPPED, Rotation.CLOCKWISE, Rotation.COUNTER_CLOCKWISE}; - private static final float[] rotationsF = {0f, 90f, 180f, -90f}; + int getSectionID(); - public static Rotation getRandomRotation() { - return rotations4[new Random().nextInt(4)]; - } + CustomCropsBlock getBlockAt(ChunkPos pos); - public static float getRandomFloatRotation() { - return rotationsF[new Random().nextInt(4)]; - } + CustomCropsBlock removeBlockAt(ChunkPos pos); + + CustomCropsBlock addBlockAt(ChunkPos pos, CustomCropsBlock block); + + boolean canPrune(); + + CustomCropsBlock[] getBlocks(); + + Map getBlockMap(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java new file mode 100644 index 0000000..af2f960 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/CustomCropsWorld.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import org.bukkit.World; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Optional; + +public interface CustomCropsWorld { + + void startTick(); + + void cancelTick(); + + CustomCropsChunk removeLazyChunkAt(ChunkCoordinate chunkCoordinate); + + WorldSetting getWorldSetting(); + + void setWorldSetting(WorldSetting setting); + + Collection getChunkStorage(); + + World getWorld(); + + String getWorldName(); + + boolean isChunkLoaded(ChunkCoordinate chunkCoordinate); + + Optional getChunkAt(ChunkCoordinate chunkCoordinate); + + void loadChunk(CustomCropsChunk chunk); + + void unloadChunk(ChunkCoordinate chunkCoordinate); + + void setInfoData(WorldInfoData infoData); + + WorldInfoData getInfoData(); + + @Nullable + Season getSeason(); + + Optional getSprinklerAt(SimpleLocation location); + + Optional getPotAt(SimpleLocation location); + + Optional getCropAt(SimpleLocation location); + + Optional getGlassAt(SimpleLocation location); + + Optional getScarecrowAt(SimpleLocation location); + + Optional getBlockAt(SimpleLocation location); + + void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount); + + void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location); + + void addWaterToPot(Pot pot, SimpleLocation location, int amount); + + void removeSprinklerAt(SimpleLocation location); + + void removePotAt(SimpleLocation location); + + void removeCropAt(SimpleLocation location); + + void removeGlassAt(SimpleLocation location); + + void removeScarecrowAt(SimpleLocation location); + + CustomCropsBlock removeAnythingAt(SimpleLocation location); + + boolean isPotReachLimit(SimpleLocation location); + + boolean isCropReachLimit(SimpleLocation location); + + boolean isSprinklerReachLimit(SimpleLocation location); + + void addPotAt(WorldPot pot, SimpleLocation location); + + void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location); + + void addCropAt(WorldCrop crop, SimpleLocation location); + + void addPointToCrop(Crop crop, SimpleLocation location, int points); + + void addGlassAt(WorldGlass glass, SimpleLocation location); + + void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java new file mode 100644 index 0000000..6f13675 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/DataBlock.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +import com.flowpowered.nbt.Tag; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.SynchronizedCompoundMap; + +public interface DataBlock { + + void setData(String key, Tag tag); + + Tag getData(String key); + + SynchronizedCompoundMap getCompoundMap(); + + SimpleLocation getLocation(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java new file mode 100644 index 0000000..2667aa7 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldCrop.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; + +public interface WorldCrop extends CustomCropsBlock { + + String getKey(); + + int getPoint(); + + void setPoint(int point); + + Crop getConfig(); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldGlass.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldGlass.java new file mode 100644 index 0000000..b4b74ce --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldGlass.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; + +public interface WorldGlass extends CustomCropsBlock { +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java new file mode 100644 index 0000000..42cd793 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldInfoData.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +import com.google.gson.annotations.SerializedName; +import net.momirealms.customcrops.api.mechanic.world.season.Season; + +public class WorldInfoData { + + @SerializedName("season") + private Season season; + @SerializedName("date") + private int date; + + public WorldInfoData(Season season, int date) { + this.season = season; + this.date = date; + } + + public static WorldInfoData empty() { + return new WorldInfoData(Season.SPRING, 1); + } + + public Season getSeason() { + if (season == null) season = Season.SPRING; + return season; + } + + public void setSeason(Season season) { + this.season = season; + } + + public int getDate() { + return date; + } + + public void setDate(int date) { + this.date = date; + } + + @Override + public String toString() { + return "WorldInfoData{" + + "season=" + season + + ", date=" + date + + '}'; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/object/CCFertilizer.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java similarity index 54% rename from api/src/main/java/net/momirealms/customcrops/api/object/CCFertilizer.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java index 8889ddd..2d35b6e 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/object/CCFertilizer.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldPot.java @@ -15,34 +15,31 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.mechanic.world.level; -/** - * Fertilizer - */ -public interface CCFertilizer { +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; - /** - * Reduce the fertilizer's usage times - * @return whether the fertilizer is used up - */ - boolean reduceTimes(); +public interface WorldPot extends CustomCropsBlock { - /** - * Get the fertilizer key in config - * @return key - */ String getKey(); - /** - * Get the remaining usage times - * @return times - */ - int getLeftTimes(); + int getWater(); - /** - * Set remaining usage times - * @param times times - */ - void setTimes(int times); + void setWater(int water); + + Fertilizer getFertilizer(); + + void setFertilizer(Fertilizer fertilizer); + + void removeFertilizer(); + + int getFertilizerTimes(); + + void setFertilizerTimes(int times); + + Pot getConfig(); + + void tickWater(CustomCropsChunk chunk); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldScarecrow.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldScarecrow.java new file mode 100644 index 0000000..a6725af --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldScarecrow.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; + +public interface WorldScarecrow extends CustomCropsBlock { +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java new file mode 100644 index 0000000..868b3b5 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSetting.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.level; + +public class WorldSetting implements Cloneable { + + private final boolean enableScheduler; + private final int minTickUnit; + private final int tickCropInterval; + private final int tickPotInterval; + private final int tickSprinklerInterval; + private final boolean offlineGrow; + private final boolean enableSeason; + private final boolean autoSeasonChange; + private final int seasonDuration; + private final int cropPerChunk; + private final int potPerChunk; + private final int sprinklerPerChunk; + private final int randomTickSpeed; + private final boolean tickCropRandomly; + private final boolean tickPotRandomly; + private final boolean tickSprinklerRandomly; + private final boolean scheduledTick; + + private WorldSetting( + boolean enableScheduler, + int minTickUnit, + boolean tickCropRandomly, + int tickCropInterval, + boolean tickPotRandomly, + int tickPotInterval, + boolean tickSprinklerRandomly, + int tickSprinklerInterval, + boolean offlineGrow, + boolean enableSeason, + boolean autoSeasonChange, + int seasonDuration, + int cropPerChunk, + int potPerChunk, + int sprinklerPerChunk, + int randomTickSpeed + ) { + this.enableScheduler = enableScheduler; + this.minTickUnit = minTickUnit; + this.tickCropInterval = tickCropInterval; + this.tickPotInterval = tickPotInterval; + this.tickSprinklerInterval = tickSprinklerInterval; + this.offlineGrow = offlineGrow; + this.enableSeason = enableSeason; + this.autoSeasonChange = autoSeasonChange; + this.seasonDuration = seasonDuration; + this.cropPerChunk = cropPerChunk; + this.potPerChunk = potPerChunk; + this.sprinklerPerChunk = sprinklerPerChunk; + this.randomTickSpeed = randomTickSpeed; + this.tickCropRandomly = tickCropRandomly; + this.tickPotRandomly = tickPotRandomly; + this.tickSprinklerRandomly = tickSprinklerRandomly; + this.scheduledTick = !(tickCropRandomly && tickPotRandomly && tickSprinklerRandomly); + } + + public static WorldSetting of( + boolean enableScheduler, + int minTickUnit, + boolean tickCropRandomly, + int tickCropInterval, + boolean tickPotRandomly, + int tickPotInterval, + boolean tickSprinklerRandomly, + int tickSprinklerInterval, + boolean offlineGrow, + boolean enableSeason, + boolean autoSeasonChange, + int seasonDuration, + int cropPerChunk, + int potPerChunk, + int sprinklerPerChunk, + int randomTickSpeed + ) { + return new WorldSetting( + enableScheduler, + minTickUnit, + tickCropRandomly, + tickCropInterval, + tickPotRandomly, + tickPotInterval, + tickSprinklerRandomly, + tickSprinklerInterval, + offlineGrow, + enableSeason, + autoSeasonChange, + seasonDuration, + cropPerChunk, + potPerChunk, + sprinklerPerChunk, + randomTickSpeed + ); + } + + public boolean isEnableScheduler() { + return enableScheduler; + } + + public int getMinTickUnit() { + return minTickUnit; + } + + public int getTickCropInterval() { + return tickCropInterval; + } + + public int getTickPotInterval() { + return tickPotInterval; + } + + public int getTickSprinklerInterval() { + return tickSprinklerInterval; + } + + public boolean isOfflineGrow() { + return offlineGrow; + } + + public boolean isEnableSeason() { + return enableSeason; + } + + public boolean isAutoSeasonChange() { + return autoSeasonChange; + } + + public int getSeasonDuration() { + return seasonDuration; + } + + public int getPotPerChunk() { + return potPerChunk; + } + + public int getCropPerChunk() { + return cropPerChunk; + } + + public int getSprinklerPerChunk() { + return sprinklerPerChunk; + } + + public int getRandomTickSpeed() { + return randomTickSpeed; + } + + public boolean isScheduledTick() { + return scheduledTick; + } + + @Override + public WorldSetting clone() { + try { + return (WorldSetting) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + + public boolean randomTickCrop() { + return tickCropRandomly; + } + + public boolean randomTickSprinkler() { + return tickSprinklerRandomly; + } + + public boolean randomTickPot() { + return tickPotRandomly; + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/object/CCSprinkler.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java similarity index 69% rename from api/src/main/java/net/momirealms/customcrops/api/object/CCSprinkler.java rename to api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java index ea8b4b1..b26f2ec 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/object/CCSprinkler.java +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/level/WorldSprinkler.java @@ -15,28 +15,18 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.mechanic.world.level; -/** - * Sprinkler - */ -public interface CCSprinkler { +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; + +public interface WorldSprinkler extends CustomCropsBlock { - /** - * Get the remaining water - * @return water amount - */ int getWater(); - /** - * Set the remaining water - * @param water water - */ void setWater(int water); - /** - * Get the sprinkler config key - * @return sprinkler key - */ String getKey(); + + Sprinkler getConfig(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/season/Season.java b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/season/Season.java new file mode 100644 index 0000000..187e161 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/mechanic/world/season/Season.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.mechanic.world.season; + +public enum Season { + + SPRING, + SUMMER, + AUTUMN, + WINTER; + + public Season getNextSeason() { + return Season.values()[(this.ordinal() + 1) % 4]; + } +} \ No newline at end of file diff --git a/api/src/main/java/net/momirealms/customcrops/api/object/CCPot.java b/api/src/main/java/net/momirealms/customcrops/api/object/CCPot.java deleted file mode 100644 index bc5017f..0000000 --- a/api/src/main/java/net/momirealms/customcrops/api/object/CCPot.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object; - -/** - * Pot - */ -public interface CCPot { - - /** - * Get the pot config key - * @return key - */ - String getKey(); - - /** - * Get the fertilizer inside the pot - * @return fertilizer - */ - CCFertilizer getFertilizer(); - - /** - * Set the fertilizer to the pot - * @param fertilizer fertilizer - */ - void setFertilizer(CCFertilizer fertilizer); - - /** - * Get the water amount - * @return water amount - */ - int getWater(); - - /** - * Whether the pot is wet - * @return wet or not - */ - boolean isWet(); - - /** - * Add water to pot - * @param amount water amount - * @return whether the pot is previously dry - */ - boolean addWater(int amount); - - /** - * Set water amount - * @param amount amount - */ - void setWater(int amount); -} diff --git a/api/src/main/java/net/momirealms/customcrops/api/object/CCWorldSeason.java b/api/src/main/java/net/momirealms/customcrops/api/scheduler/CancellableTask.java similarity index 72% rename from api/src/main/java/net/momirealms/customcrops/api/object/CCWorldSeason.java rename to api/src/main/java/net/momirealms/customcrops/api/scheduler/CancellableTask.java index 041afc6..d968878 100644 --- a/api/src/main/java/net/momirealms/customcrops/api/object/CCWorldSeason.java +++ b/api/src/main/java/net/momirealms/customcrops/api/scheduler/CancellableTask.java @@ -15,22 +15,19 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object; +package net.momirealms.customcrops.api.scheduler; -/** - * Season - */ -public interface CCWorldSeason { +public interface CancellableTask { /** - * SPRING, SUMMER, AUTUMN, WINTER - * @return season + * Cancel the task */ - String getSeason(); + void cancel(); /** - * Get the season display name - * @return display name + * Get if the task is cancelled or not + * + * @return cancelled or not */ - String getDisplay(); + boolean isCancelled(); } diff --git a/api/src/main/java/net/momirealms/customcrops/api/scheduler/Scheduler.java b/api/src/main/java/net/momirealms/customcrops/api/scheduler/Scheduler.java new file mode 100644 index 0000000..2e72170 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/scheduler/Scheduler.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.scheduler; + +import org.bukkit.Location; + +import java.util.concurrent.TimeUnit; + +public interface Scheduler { + + /** + * Runs a task synchronously on the main server thread or region thread. + * + * @param runnable The task to run. + * @param location The location associated with the task. + */ + void runTaskSync(Runnable runnable, Location location); + + /** + * Runs a task synchronously with a specified delay and period. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delayTicks The delay in ticks before the first execution. + * @param periodTicks The period between subsequent executions in ticks. + * @return A CancellableTask for managing the scheduled task. + */ + CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks); + + /** + * Runs a task asynchronously with a specified delay. + * + * @param runnable The task to run. + * @param delay The delay before the task execution. + * @param timeUnit The time unit for the delay. + * @return A CancellableTask for managing the scheduled task. + */ + CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit); + + /** + * Runs a task asynchronously. + * + * @param runnable The task to run. + */ + void runTaskAsync(Runnable runnable); + + /** + * Runs a task synchronously with a specified delay. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delay The delay before the task execution. + * @param timeUnit The time unit for the delay. + * @return A CancellableTask for managing the scheduled task. + */ + CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit timeUnit); + + /** + * Runs a task synchronously with a specified delay in ticks. + * + * @param runnable The task to run. + * @param location The location associated with the task. + * @param delayTicks The delay in ticks before the task execution. + * @return A CancellableTask for managing the scheduled task. + */ + CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks); + + /** + * Runs a task asynchronously with a specified delay and period. + * + * @param runnable The task to run. + * @param delay The delay before the first execution. + * @param period The period between subsequent executions. + * @param timeUnit The time unit for the delay and period. + * @return A CancellableTask for managing the scheduled task. + */ + CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit); +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java new file mode 100644 index 0000000..e865ce0 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/util/LocationUtils.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.util; + +import org.bukkit.Bukkit; +import org.bukkit.Location; + +public class LocationUtils { + + private LocationUtils() {} + + /** + * Calculates the Euclidean distance between two locations in 3D space. + * + * @param location1 The first location + * @param location2 The second location + * @return The Euclidean distance between the two locations + */ + public static double getDistance(Location location1, Location location2) { + return Math.sqrt(Math.pow(location2.getX() - location1.getX(), 2) + + Math.pow(location2.getY() - location1.getY(), 2) + + Math.pow(location2.getZ() - location1.getZ(), 2) + ); + } + + public static Location getAnyLocationInstance() { + return new Location(Bukkit.getWorlds().get(0), 0, 64, 0); + } +} diff --git a/api/src/main/java/net/momirealms/customcrops/api/util/LogUtils.java b/api/src/main/java/net/momirealms/customcrops/api/util/LogUtils.java new file mode 100644 index 0000000..1f03d49 --- /dev/null +++ b/api/src/main/java/net/momirealms/customcrops/api/util/LogUtils.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.api.util; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import org.jetbrains.annotations.NotNull; + +import java.util.logging.Level; + +/** + * Utility class for logging messages with various log levels. + */ +public final class LogUtils { + + private LogUtils() {} + + /** + * Log an informational message. + * + * @param message The message to log. + */ + public static void info(@NotNull String message) { + CustomCropsPlugin.getInstance().getLogger().info(message); + } + + /** + * Log a warning message. + * + * @param message The message to log. + */ + public static void warn(@NotNull String message) { + CustomCropsPlugin.getInstance().getLogger().warning(message); + } + + /** + * Log a severe error message. + * + * @param message The message to log. + */ + public static void severe(@NotNull String message) { + CustomCropsPlugin.getInstance().getLogger().severe(message); + } + + /** + * Log a warning message with a throwable exception. + * + * @param message The message to log. + * @param throwable The throwable exception to log. + */ + public static void warn(@NotNull String message, Throwable throwable) { + CustomCropsPlugin.getInstance().getLogger().log(Level.WARNING, message, throwable); + } + + /** + * Log a severe error message with a throwable exception. + * + * @param message The message to log. + * @param throwable The throwable exception to log. + */ + public static void severe(@NotNull String message, Throwable throwable) { + CustomCropsPlugin.getInstance().getLogger().log(Level.SEVERE, message, throwable); + } +} diff --git a/build.gradle.kts b/build.gradle.kts index 0c162f7..7355535 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ plugins { allprojects { project.group = "net.momirealms" - project.version = "3.3.1.16" + project.version = "3.4.0.0" apply() apply(plugin = "java") @@ -21,8 +21,8 @@ allprojects { } repositories { - maven("https://maven.aliyun.com/repository/public/") mavenCentral() + maven("https://maven.aliyun.com/repository/public/") maven("https://betonquest.org/nexus/repository/betonquest/") maven("https://maven.enginehub.org/repo/") maven("https://oss.sonatype.org/content/groups/public/") @@ -40,21 +40,9 @@ allprojects { maven("https://r.irepo.space/maven/") maven("https://repo.auxilor.io/repository/maven-public/") maven("https://nexus.betonquest.org/repository/betonquest/") - } - - dependencies { - testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") - testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.3") - testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.3") - } - - tasks.processResources { - val props = mapOf("version" to version) - inputs.properties(props) - filteringCharset = "UTF-8" - filesMatching("plugin.yml") { - expand(props) - } + maven("https://repo.infernalsuite.com/repository/maven-releases/") + maven("https://repo.rapture.pw/repository/maven-releases/") + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") } } diff --git a/gradle.properties b/gradle.properties index d508eeb..16d5100 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ #systemProp.socks.proxyHost=127.0.0.1 #systemProp.socks.proxyPort=7890 -# + #systemProp.http.proxyHost=127.0.0.1 #systemProp.http.proxyPort=7890 -# + #systemProp.https.proxyHost=127.0.0.1 #systemProp.https.proxyPort=7890 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d1da74a..269ed93 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 163308a..d35513a 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,45 +1,73 @@ dependencies { - compileOnly(fileTree("libs")) + // Platform + compileOnly("dev.folia:folia-api:1.20.1-R0.1-SNAPSHOT") + compileOnly("com.infernalsuite.aswm:api:1.20.4-R0.1-SNAPSHOT") + + // Command + compileOnly("dev.jorel:commandapi-bukkit-core:9.3.0") + + // Common hooks compileOnly("me.clip:placeholderapi:2.11.5") - compileOnly("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT") - compileOnly("dev.dejvokep:boosted-yaml:1.3.1") - compileOnly("commons-io:commons-io:2.11.0") - compileOnly("com.google.code.gson:gson:2.10.1") - compileOnly("com.github.LoneDev6:api-itemsadder:3.4.1e") - compileOnly("com.github.oraxen:oraxen:1.168.0") - compileOnly("io.lumine:Mythic-Dist:5.2.1") - compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT") - compileOnly("com.willfp:eco:6.65.1") - compileOnly("com.willfp:EcoJobs:3.13.0") - compileOnly("net.objecthunter:exp4j:0.4.8") - compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT") - compileOnly("pers.neige.neigeitems:NeigeItems:1.14.23") - compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.21") - compileOnly("com.willfp:EcoSkills:3.36.1") - compileOnly("com.github.Zrips:Jobs:4.17.2") + compileOnly("com.comphenix.protocol:ProtocolLib:5.1.0") compileOnly("com.github.MilkBowl:VaultAPI:1.7") - compileOnly("com.comphenix.protocol:ProtocolLib:5.0.0") + + // Utils + compileOnly("dev.dejvokep:boosted-yaml:1.3.2") + compileOnly("commons-io:commons-io:2.15.1") + compileOnly("com.google.code.gson:gson:2.10.1") + compileOnly("net.objecthunter:exp4j:0.4.8") + + // eco + compileOnly("com.willfp:eco:6.67.2") + compileOnly("com.willfp:EcoJobs:3.47.1") + compileOnly("com.willfp:EcoSkills:3.21.0") + compileOnly("com.willfp:libreforge:4.48.1") + compileOnly("net.Indyuce:MMOCore-API:1.12-SNAPSHOT") + compileOnly("com.github.Archy-X:AureliumSkills:Beta1.3.24") + compileOnly("com.github.Zrips:Jobs:4.17.2") + compileOnly("dev.aurelium:auraskills-api-bukkit:2.0.0-SNAPSHOT") + + // Items + compileOnly("com.github.LoneDev6:api-itemsadder:3.6.2-beta-r3-b") + compileOnly("com.github.oraxen:oraxen:1.168.0") + compileOnly("pers.neige.neigeitems:NeigeItems:1.16.24") + compileOnly("net.Indyuce:MMOItems-API:6.9.2-SNAPSHOT") + compileOnly("io.lumine:MythicLib-dist:1.6-SNAPSHOT") + compileOnly("io.lumine:Mythic-Dist:5.3.5") + + // Quests compileOnly("org.betonquest:betonquest:2.0.0") + compileOnly(files("libs/BattlePass-4.0.6-api.jar")) + compileOnly(files("libs/ClueScrolls-api.jar")) + compileOnly(files("libs/AdvancedSeasons-API.jar")) + compileOnly(files("libs/zaphkiel-2.0.24.jar")) + compileOnly(files("libs/mcMMO-api.jar")) + compileOnly(files("libs/RealisticSeasons-api.jar")) + implementation(project(":api")) implementation("net.kyori:adventure-api:4.15.0") implementation("net.kyori:adventure-platform-bukkit:4.3.2") - implementation("net.kyori:adventure-text-minimessage:4.15.0") - implementation("net.kyori:adventure-text-serializer-legacy:4.15.0") - implementation("com.github.Xiao-MoMi:AntiGriefLib:0.7") - implementation("de.tr7zw:item-nbt-api:2.12.2") - implementation("org.bstats:bstats-bukkit:3.0.2") - implementation(files("libs/BiomeAPI.jar")) + compileOnly("com.github.Xiao-MoMi:AntiGriefLib:0.7") + compileOnly("com.github.Xiao-MoMi:BiomeAPI:0.2") + compileOnly("net.kyori:adventure-text-minimessage:4.15.0") + compileOnly("net.kyori:adventure-text-serializer-legacy:4.15.0") + compileOnly("de.tr7zw:item-nbt-api:2.12.2") + compileOnly("org.bstats:bstats-bukkit:3.0.2") + implementation("org.lz4:lz4-java:1.8.0") + implementation("com.flowpowered:flow-nbt:2.0.2") + implementation("com.github.luben:zstd-jni:1.5.5-11") } tasks { shadowJar { - relocate ("de.tr7zw.changeme", "net.momirealms.customcrops.libraries") - relocate ("de.tr7zw.annotations", "net.momirealms.customcrops.libraries.annotations") + relocate ("de.tr7zw.changeme", "net.momirealms.customcrops.libraries.changeme") + relocate ("dev.jorel.commandapi", "net.momirealms.customcrops.libraries.commandapi") relocate ("net.kyori", "net.momirealms.customcrops.libraries") + relocate ("org.objenesis", "net.momirealms.customcrops.libraries.objenesis") relocate ("org.bstats", "net.momirealms.customcrops.libraries.bstats") relocate ("net.momirealms.biomeapi", "net.momirealms.customcrops.libraries.biomeapi") - relocate ("net.momirealms.antigrieflib", "net.momirealms.customcrops.libraries.antigrieflib") + relocate ("net.momirealms.antigrieflib", "net.momirealms.customcrops.libraries.antigrieflib") } -} +} \ No newline at end of file diff --git a/plugin/libs/BiomeAPI.jar b/plugin/libs/BiomeAPI.jar deleted file mode 100644 index 59096aa..0000000 Binary files a/plugin/libs/BiomeAPI.jar and /dev/null differ diff --git a/plugin/libs/zaphkiel-2.0.24.jar b/plugin/libs/zaphkiel-2.0.24.jar new file mode 100644 index 0000000..ac48bf0 Binary files /dev/null and b/plugin/libs/zaphkiel-2.0.24.jar differ diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCrops.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCrops.java deleted file mode 100644 index eb2d8ce..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/CustomCrops.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops; - -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.ProtocolManager; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import net.momirealms.antigrieflib.AntiGriefLib; -import net.momirealms.customcrops.api.CustomCropsAPIImpl; -import net.momirealms.customcrops.api.CustomCropsPlugin; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.api.object.crop.CropManager; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerManager; -import net.momirealms.customcrops.api.object.hologram.HologramManager; -import net.momirealms.customcrops.api.object.pot.PotManager; -import net.momirealms.customcrops.api.object.scheduler.Scheduler; -import net.momirealms.customcrops.api.object.season.SeasonManager; -import net.momirealms.customcrops.api.object.sprinkler.SprinklerManager; -import net.momirealms.customcrops.api.object.wateringcan.WateringCanManager; -import net.momirealms.customcrops.api.object.world.WorldDataManager; -import net.momirealms.customcrops.command.CustomCropsCommand; -import net.momirealms.customcrops.customplugin.Platform; -import net.momirealms.customcrops.customplugin.PlatformInterface; -import net.momirealms.customcrops.customplugin.PlatformManager; -import net.momirealms.customcrops.customplugin.itemsadder.ItemsAdderPluginImpl; -import net.momirealms.customcrops.customplugin.oraxen.OraxenPluginImpl; -import net.momirealms.customcrops.helper.LibraryLoader; -import net.momirealms.customcrops.helper.VersionHelper; -import net.momirealms.customcrops.integration.IntegrationManager; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bstats.bukkit.Metrics; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.PluginCommand; -import org.bukkit.plugin.PluginManager; - -import java.util.TimeZone; - -public final class CustomCrops extends CustomCropsPlugin { - - private static BukkitAudiences adventure; - private static CustomCrops plugin; - private static ProtocolManager protocolManager; - private Platform platform; - private PlatformInterface platformInterface; - private CropManager cropManager; - private IntegrationManager integrationManager; - private WorldDataManager worldDataManager; - private SprinklerManager sprinklerManager; - private WateringCanManager wateringCanManager; - private FertilizerManager fertilizerManager; - private SeasonManager seasonManager; - private PotManager potManager; - private ConfigManager configManager; - private MessageManager messageManager; - private PlatformManager platformManager; - private HologramManager hologramManager; - private VersionHelper versionHelper; - private Scheduler scheduler; - private AntiGriefLib antiGriefLib; - - @Override - public void onLoad(){ - plugin = this; - instance = this; - this.loadLibs(); - this.antiGriefLib = AntiGriefLib.builder(this) - .ignoreOP(true) - .build(); - } - - @Override - public void onEnable() { - adventure = BukkitAudiences.create(this); - protocolManager = ProtocolLibrary.getProtocolManager(); - this.versionHelper = new VersionHelper(this); - if (versionHelper.isSpigot()) { - AdventureUtils.consoleMessage("========================[CustomCrops]========================="); - AdventureUtils.consoleMessage(" Spigot is not officially supported by CustomCrops"); - AdventureUtils.consoleMessage(" Please use Paper or its forks"); - AdventureUtils.consoleMessage(" Paper download link: https://papermc.io/downloads"); - AdventureUtils.consoleMessage("=============================================================="); - Bukkit.getPluginManager().disablePlugin(this); - return; - } - - if (!this.loadPlatform()) return; - this.registerCommands(); - AdventureUtils.consoleMessage("[CustomCrops] Running on " + Bukkit.getVersion()); - - this.scheduler = new Scheduler(this); - this.configManager = new ConfigManager(this); - this.messageManager = new MessageManager(this); - this.cropManager = new CropManager(this); - this.integrationManager = new IntegrationManager(this); - this.seasonManager = new SeasonManager(this); - this.worldDataManager = new WorldDataManager(this); - this.sprinklerManager = new SprinklerManager(this); - this.wateringCanManager = new WateringCanManager(this); - this.fertilizerManager = new FertilizerManager(this); - this.potManager = new PotManager(this); - this.hologramManager = new HologramManager(this); - this.platformManager = new PlatformManager(this); - super.customCropsAPI = new CustomCropsAPIImpl(this); - - this.reload(); - - for (World world : Bukkit.getWorlds()) { - this.worldDataManager.loadWorld(world); - } - - AdventureUtils.consoleMessage("[CustomCrops] Plugin Enabled!"); - if (ConfigManager.enableBStats) new Metrics(this, 16593); - if (ConfigManager.checkUpdate) this.versionHelper.checkUpdate(); - - antiGriefLib.init(); - } - - public void reload() { - this.configManager.unload(); - this.messageManager.unload(); - this.cropManager.unload(); - this.integrationManager.unload(); - this.worldDataManager.unload(); - this.sprinklerManager.unload(); - this.wateringCanManager.unload(); - this.fertilizerManager.unload(); - this.potManager.unload(); - this.seasonManager.unload(); - this.platformManager.unload(); - this.hologramManager.unload(); - - this.configManager.load(); - this.messageManager.load(); - this.integrationManager.load(); - this.cropManager.load(); - this.worldDataManager.load(); - this.sprinklerManager.load(); - this.wateringCanManager.load(); - this.fertilizerManager.load(); - this.potManager.load(); - this.seasonManager.load(); - this.platformManager.load(); - this.hologramManager.load(); - } - - @Override - public void onDisable() { - if (adventure != null) adventure.close(); - if (this.cropManager != null) this.cropManager.unload(); - if (this.worldDataManager != null) this.worldDataManager.disable(); - if (this.seasonManager != null) this.seasonManager.unload(); - if (this.sprinklerManager != null) this.sprinklerManager.unload(); - if (this.wateringCanManager != null) this.wateringCanManager.unload(); - if (this.fertilizerManager != null) this.fertilizerManager.unload(); - if (this.platformManager != null) this.platformManager.unload(); - if (this.potManager != null) this.potManager.unload(); - if (this.messageManager != null) this.messageManager.unload(); - if (this.configManager != null) this.configManager.unload(); - if (this.integrationManager != null) this.integrationManager.unload(); - if (this.hologramManager != null) this.hologramManager.unload(); - if (this.scheduler != null) this.scheduler.disable(); - } - - private void loadLibs() { - TimeZone timeZone = TimeZone.getDefault(); - String libRepo = timeZone.getID().startsWith("Asia") ? "https://maven.aliyun.com/repository/public/" : "https://repo.maven.apache.org/maven2/"; - LibraryLoader.load("dev.dejvokep","boosted-yaml","1.3.1", libRepo); - LibraryLoader.load("commons-io","commons-io","2.11.0", libRepo); - LibraryLoader.load("net.objecthunter","exp4j","0.4.8", libRepo); - } - - private void registerCommands() { - CustomCropsCommand customCropsCommand = new CustomCropsCommand(); - PluginCommand pluginCommand = Bukkit.getPluginCommand("customcrops"); - if (pluginCommand != null) { - pluginCommand.setExecutor(customCropsCommand); - pluginCommand.setTabCompleter(customCropsCommand); - } - } - - private boolean loadPlatform() { - PluginManager pluginManager = Bukkit.getPluginManager(); - if (pluginManager.getPlugin("ItemsAdder") != null) { - this.platform = Platform.ItemsAdder; - this.platformInterface = new ItemsAdderPluginImpl(); - } - else if (pluginManager.getPlugin("Oraxen") != null) { - this.platform = Platform.Oraxen; - this.platformInterface = new OraxenPluginImpl(); - } - if (this.platform == null) { - AdventureUtils.consoleMessage("========================[CustomCrops]========================="); - AdventureUtils.consoleMessage(" Please install ItemsAdder or Oraxen as dependency."); - AdventureUtils.consoleMessage(" ItemsAdder Link: https://www.spigotmc.org/resources/73355/"); - AdventureUtils.consoleMessage(" Oraxen link: https://www.spigotmc.org/resources/72448/"); - AdventureUtils.consoleMessage("=============================================================="); - Bukkit.getPluginManager().disablePlugin(this); - return false; - } else { - AdventureUtils.consoleMessage("[CustomCrops] Platform: " + platform.name()); - return true; - } - } - - public static BukkitAudiences getAdventure() { - return adventure; - } - - public static CustomCrops getInstance() { - return plugin; - } - - public static ProtocolManager getProtocolManager() { - return protocolManager; - } - - public CropManager getCropManager() { - return cropManager; - } - - public VersionHelper getVersionHelper() { - return versionHelper; - } - - public Platform getPlatform() { - return platform; - } - - public IntegrationManager getIntegrationManager() { - return integrationManager; - } - - public PlatformInterface getPlatformInterface() { - return platformInterface; - } - - public WorldDataManager getWorldDataManager() { - return worldDataManager; - } - - public SprinklerManager getSprinklerManager() { - return sprinklerManager; - } - - public PotManager getPotManager() { - return potManager; - } - - public WateringCanManager getWateringCanManager() { - return wateringCanManager; - } - - public FertilizerManager getFertilizerManager() { - return fertilizerManager; - } - - public SeasonManager getSeasonManager() { - return seasonManager; - } - - public ConfigManager getConfigManager() { - return configManager; - } - - public MessageManager getMessageManager() { - return messageManager; - } - - public PlatformManager getPlatformManager() { - return platformManager; - } - - public HologramManager getHologramManager() { - return hologramManager; - } - - public Scheduler getScheduler() { - return scheduler; - } - - public AntiGriefLib getAntiGriefLib() { - return antiGriefLib; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java new file mode 100644 index 0000000..7eb37be --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/CustomCropsPluginImpl.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops; + +import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion; +import de.tr7zw.changeme.nbtapi.utils.VersionChecker; +import net.momirealms.antigrieflib.AntiGriefLib; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.event.CustomCropsReloadEvent; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.CoolDownManager; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.compatibility.IntegrationManagerImpl; +import net.momirealms.customcrops.libraries.classpath.ReflectionClassPathAppender; +import net.momirealms.customcrops.libraries.dependencies.Dependency; +import net.momirealms.customcrops.libraries.dependencies.DependencyManager; +import net.momirealms.customcrops.libraries.dependencies.DependencyManagerImpl; +import net.momirealms.customcrops.manager.*; +import net.momirealms.customcrops.mechanic.action.ActionManagerImpl; +import net.momirealms.customcrops.mechanic.condition.ConditionManagerImpl; +import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; +import net.momirealms.customcrops.mechanic.requirement.RequirementManagerImpl; +import net.momirealms.customcrops.mechanic.world.WorldManagerImpl; +import net.momirealms.customcrops.scheduler.SchedulerImpl; +import net.momirealms.customcrops.utils.EventUtils; +import org.bstats.bukkit.Metrics; +import org.bukkit.Bukkit; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +public class CustomCropsPluginImpl extends CustomCropsPlugin { + + private DependencyManager dependencyManager; + private PacketManager packetManager; + private CommandManager commandManager; + private HologramManager hologramManager; + private AntiGriefLib antiGriefLib; + + @Override + public void onLoad() { + this.dependencyManager = new DependencyManagerImpl(this, new ReflectionClassPathAppender(this.getClassLoader())); + this.dependencyManager.loadDependencies(new ArrayList<>( + List.of( + Dependency.GSON, + Dependency.SLF4J_API, + Dependency.SLF4J_SIMPLE, + Dependency.COMMAND_API, + Dependency.NBT_API, + Dependency.BOOSTED_YAML, + Dependency.ADVENTURE_TEXT_MINIMESSAGE, + Dependency.ADVENTURE_LEGACY_SERIALIZER, + Dependency.BSTATS_BASE, + Dependency.BSTATS_BUKKIT, + Dependency.BIOME_API, + Dependency.ANTI_GRIEF + ) + )); + + this.antiGriefLib = AntiGriefLib.builder(this) + .silentLogs(true) + .ignoreOP(true) + .build(); + } + + @Override + public void onEnable() { + instance = this; + this.versionManager = new VersionManagerImpl(this); + this.adventure = new AdventureManagerImpl(this); + this.scheduler = new SchedulerImpl(this); + this.configManager = new ConfigManagerImpl(this); + this.integrationManager = new IntegrationManagerImpl(this); + this.conditionManager = new ConditionManagerImpl(this); + this.actionManager = new ActionManagerImpl(this); + this.requirementManager = new RequirementManagerImpl(this); + this.coolDownManager = new CoolDownManager(this); + this.worldManager = new WorldManagerImpl(this); + this.itemManager = new ItemManagerImpl(this, antiGriefLib); + this.messageManager = new MessageManagerImpl(this); + this.packetManager = new PacketManager(this); + this.commandManager = new CommandManager(this); + this.placeholderManager = new PlaceholderManagerImpl(this); + this.hologramManager = new HologramManager(this); + this.commandManager.init(); + this.antiGriefLib.init(); + this.integrationManager.init(); + this.disableNBTAPILogs(); + this.reload(); + this.worldManager.init(); + if (ConfigManager.metrics()) new Metrics(this, 16593); + if (ConfigManager.checkUpdate()) { + this.versionManager.checkUpdate().thenAccept(result -> { + if (!result) this.getAdventure().sendConsoleMessage("[CustomCrops] You are using the latest version."); + else this.getAdventure().sendConsoleMessage("[CustomCrops] Update is available: https://polymart.org/resource/2625"); + }); + } + } + + @Override + public void onDisable() { + this.commandManager.disable(); + this.adventure.disable(); + this.requirementManager.disable(); + this.actionManager.disable(); + this.worldManager.disable(); + this.itemManager.disable(); + this.conditionManager.disable(); + this.coolDownManager.disable(); + this.placeholderManager.disable(); + ((SchedulerImpl) scheduler).shutdown(); + instance = null; + } + + @Override + public void reload() { + this.configManager.reload(); + this.messageManager.reload(); + this.itemManager.reload(); + this.worldManager.reload(); + this.actionManager.reload(); + this.requirementManager.reload(); + this.conditionManager.reload(); + this.coolDownManager.reload(); + this.placeholderManager.reload(); + this.hologramManager.reload(); + ((SchedulerImpl) scheduler).reload(); + EventUtils.fireAndForget(new CustomCropsReloadEvent(this)); + } + + /** + * Disable NBT API logs + */ + private void disableNBTAPILogs() { + MinecraftVersion.disableBStats(); + MinecraftVersion.disableUpdateCheck(); + VersionChecker.hideOk = true; + try { + Field field = MinecraftVersion.class.getDeclaredField("version"); + field.setAccessible(true); + MinecraftVersion minecraftVersion; + try { + minecraftVersion = MinecraftVersion.valueOf(getVersionManager().getServerVersion().replace("v", "MC")); + } catch (IllegalArgumentException ex) { + minecraftVersion = MinecraftVersion.UNKNOWN; + } + field.set(MinecraftVersion.class, minecraftVersion); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + boolean hasGsonSupport; + try { + Class.forName("com.google.gson.Gson"); + hasGsonSupport = true; + } catch (Exception ex) { + hasGsonSupport = false; + } + try { + Field field= MinecraftVersion.class.getDeclaredField("hasGsonSupport"); + field.setAccessible(true); + field.set(Boolean.class, hasGsonSupport); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Override + public void debug(String debug) { + if (ConfigManager.debug()) { + LogUtils.info(debug); + } + } + + public DependencyManager getDependencyManager() { + return dependencyManager; + } + + public PacketManager getPacketManager() { + return packetManager; + } + + public HologramManager getHologramManager() { + return hologramManager; + } + + @Override + public boolean isHookedPluginEnabled(String plugin) { + return Bukkit.getPluginManager().isPluginEnabled(plugin); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/CustomCropsAPIImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/CustomCropsAPIImpl.java deleted file mode 100644 index 41ca4ee..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/CustomCropsAPIImpl.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.CCGrowingCrop; -import net.momirealms.customcrops.api.object.CCPot; -import net.momirealms.customcrops.api.object.CCSprinkler; -import net.momirealms.customcrops.api.object.CCWorldSeason; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.api.object.season.SeasonData; -import net.momirealms.customcrops.api.object.world.CCWorld; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.Location; -import org.bukkit.World; -import org.jetbrains.annotations.Nullable; - -import java.util.Locale; - -public class CustomCropsAPIImpl implements CustomCropsAPI { - - private static CustomCropsAPIImpl instance; - private final CustomCrops plugin; - - public CustomCropsAPIImpl(CustomCrops plugin) { - this.plugin = plugin; - instance = this; - } - - public static CustomCropsAPIImpl getInstance() { - return instance; - } - - @Nullable - public CCPot getPotAt(Location location) { - return plugin.getWorldDataManager().getPotData(SimpleLocation.getByBukkitLocation(location)); - } - - @Nullable - public CCGrowingCrop getCropAt(Location location) { - return plugin.getWorldDataManager().getCropData(SimpleLocation.getByBukkitLocation(location)); - } - - public boolean isGreenhouseGlass(Location location) { - return plugin.getWorldDataManager().isGreenhouse(SimpleLocation.getByBukkitLocation(location)); - } - - public boolean hasScarecrowInChunk(Location location) { - return plugin.getWorldDataManager().hasScarecrow(SimpleLocation.getByBukkitLocation(location)); - } - - public CCSprinkler getSprinklerAt(Location location) { - return plugin.getWorldDataManager().getSprinklerData(SimpleLocation.getByBukkitLocation(location)); - } - - public void setSeason(String world, String season) { - SeasonData seasonData = plugin.getSeasonManager().getSeasonData(world); - if (seasonData != null) { - seasonData.changeSeason(CCSeason.valueOf(season.toUpperCase(Locale.ENGLISH))); - } - } - - public void setDate(String world, int date) { - SeasonData seasonData = plugin.getSeasonManager().getSeasonData(world); - if (seasonData != null) { - seasonData.setDate(date); - } - } - - public void addDate(String world) { - SeasonData seasonData = plugin.getSeasonManager().getSeasonData(world); - if (seasonData != null) { - seasonData.addDate(); - } - } - - @Nullable - public CCWorldSeason getSeason(String world) { - SeasonData seasonData = plugin.getSeasonManager().getSeasonData(world); - if (seasonData != null) { - return seasonData.getSeason(); - } - return null; - } - - public void grow(World world, int seconds) { - CustomCrops.getInstance().getScheduler().runTaskAsync(() -> { - CCWorld ccworld = CustomCrops.getInstance().getWorldDataManager().getWorld(world.getName()); - if (ccworld != null) { - ccworld.scheduleConsumeTask(seconds); - } - }); - } - - public void sprinklerWork(World world, int seconds) { - CustomCrops.getInstance().getScheduler().runTaskAsync(() -> { - CCWorld ccworld = CustomCrops.getInstance().getWorldDataManager().getWorld(world.getName()); - if (ccworld != null) { - ccworld.scheduleSprinklerWork(seconds); - } - }); - } - - public void consume(World world, int seconds) { - CustomCrops.getInstance().getScheduler().runTaskAsync(() -> { - CCWorld ccworld = CustomCrops.getInstance().getWorldDataManager().getWorld(world.getName()); - if (ccworld != null) { - ccworld.scheduleConsumeTask(seconds); - } - }); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/BoneMeal.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/BoneMeal.java deleted file mode 100644 index dd69e51..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/BoneMeal.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object; - -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.CustomCrops; -import org.bukkit.Particle; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; - -public class BoneMeal { - - private final String item; - private final String returned; - private final ArrayList> pairs; - private final Sound sound; - private final Particle particle; - - public BoneMeal( - String item, - @Nullable String returned, - @NotNull ArrayList> pairs, - @Nullable Sound sound, - @Nullable Particle particle - ) { - this.item = item; - this.returned = returned; - this.pairs = pairs; - this.sound = sound; - this.particle = particle; - } - - public boolean isRightItem(String id) { - return item.equals(id); - } - - public ItemStack getReturned() { - if (returned == null) return null; - return CustomCrops.getInstance().getIntegrationManager().build(returned); - } - - public int getPoint() { - for (Pair pair : pairs) { - if (Math.random() < pair.left()) { - return pair.right(); - } - } - return 0; - } - - @Nullable - public Sound getSound() { - return sound; - } - - @Nullable - public Particle getParticle() { - return particle; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/CrowTask.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/CrowTask.java deleted file mode 100644 index f898329..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/CrowTask.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.util.FakeEntityUtils; -import org.bukkit.Location; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.util.Vector; - -import java.util.concurrent.ThreadLocalRandom; - -public class CrowTask extends BukkitRunnable { - - private int timer; - private final int entityID; - private final Vector vectorUp; - private final Location cropLoc; - private final Player player; - private final float yaw; - private final ItemStack fly; - - public CrowTask(Player player, Location crop_location, String fly_model, String stand_model) { - this.cropLoc = crop_location.clone(); - this.timer = 0; - this.fly = CustomCrops.getInstance().getIntegrationManager().build(fly_model); - this.player = player; - this.entityID = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); - this.yaw = ThreadLocalRandom.current().nextInt(361) - 180; - Location relative = crop_location.clone().subtract(crop_location.clone().add(10 * Math.sin((Math.PI * yaw)/180), 10, - 10 * Math.cos((Math.PI * yaw)/180))); - this.vectorUp = new Vector(relative.getX() / 75, 0.1, relative.getZ() / 75); - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getSpawnPacket(entityID, crop_location, EntityType.ARMOR_STAND)); - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getVanishArmorStandMetaPacket(entityID)); - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getEquipPacket(entityID, CustomCrops.getInstance().getIntegrationManager().build(stand_model))); - } - - @Override - public void run() { - timer++; - if (timer == 40) { - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getEquipPacket(entityID, fly)); - } else if (timer > 40) { - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getTeleportPacket(entityID, cropLoc.add(vectorUp), yaw)); - } - if (timer > 100) { - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getDestroyPacket(entityID)); - cancel(); - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/InteractCrop.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/InteractCrop.java deleted file mode 100644 index a19589f..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/InteractCrop.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.api.object.requirement.CurrentState; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -public class InteractCrop { - - private final boolean consume; - private final String id; - private final String returned; - private final Action[] actions; - private final Requirement[] requirements; - - public InteractCrop(@Nullable String id, boolean consume, @Nullable String returned, @Nullable Action[] actions, @Nullable Requirement[] requirements) { - this.consume = consume; - this.id = id; - this.returned = returned; - this.actions = actions; - this.requirements = requirements; - } - - public boolean isRightItem(String item) { - if (id == null || id.equals("*")) return true; - return item.equals(id); - } - - @Nullable - public ItemStack getReturned() { - if (returned == null) return null; - return CustomCrops.getInstance().getIntegrationManager().build(returned); - } - - public boolean isConsumed() { - return consume; - } - - public Action[] getActions() { - return actions; - } - - public boolean canInteract(Player player, Location location) { - if (requirements == null) return true; - CurrentState currentState = new CurrentState(location, player); - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(currentState)) { - return false; - } - } - return true; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java deleted file mode 100644 index 97a2878..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/OfflineReplaceTask.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.momirealms.customcrops.api.object; - -import java.io.Serializable; - -public class OfflineReplaceTask implements Serializable { - - private final String id; - private final ItemType itemType; - private final ItemMode itemMode; - - public OfflineReplaceTask(String id, ItemType itemType, ItemMode itemMode) { - this.id = id; - this.itemMode = itemMode; - this.itemType = itemType; - } - - public String getId() { - return id; - } - - public ItemMode getItemMode() { - return itemMode; - } - - public ItemType getItemType() { - return itemType; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ActionBarImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ActionBarImpl.java deleted file mode 100644 index 46a4fe7..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ActionBarImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public record ActionBarImpl(String message, double chance) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player == null || Math.random() > chance) return; - AdventureUtils.playerActionbar(player, - message.replace("{player}", player.getName()) - .replace("{world}", player.getWorld().getName()) - .replace("{x}", cropLoc == null ? "" : String.valueOf(cropLoc.getX())) - .replace("{y}", cropLoc == null ? "" : String.valueOf(cropLoc.getY())) - .replace("{z}", cropLoc == null ? "" : String.valueOf(cropLoc.getZ())) - ); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/BreakImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/BreakImpl.java deleted file mode 100644 index 49099a5..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/BreakImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.event.CropBreakEvent; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.crop.CropConfig; -import net.momirealms.customcrops.api.object.crop.StageConfig; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.CompletableFuture; - -public class BreakImpl implements Action { - - private final boolean triggerAction; - private final String stageID; - - public BreakImpl(boolean triggerAction, @Nullable String stageID) { - this.triggerAction = triggerAction; - this.stageID = stageID; - } - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (cropLoc == null || stageID == null) return; - CropConfig cropConfig = CustomCrops.getInstance().getCropManager().getCropConfigByStage(stageID); - Location bLoc = cropLoc.getBukkitLocation(); - if (bLoc == null || cropConfig == null) return; - if (player != null) { - CropBreakEvent cropBreakEvent = new CropBreakEvent(player, cropConfig.getKey(), stageID, bLoc); - Bukkit.getPluginManager().callEvent(cropBreakEvent); - if (cropBreakEvent.isCancelled()) { - return; - } - CustomCrops.getInstance().getPlatformInterface().removeAnyThingAt(bLoc); - CustomCrops.getInstance().getWorldDataManager().removeCropData(cropLoc); - doTriggerActions(player, cropLoc, itemMode); - } else { - CompletableFuture asyncGetChunk = bLoc.getWorld().getChunkAtAsync(bLoc.getBlockX() >> 4, bLoc.getBlockZ() >> 4); - if (itemMode == ItemMode.ITEM_FRAME || itemMode == ItemMode.ITEM_DISPLAY) { - CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { - chunk.getEntities(); - return chunk.isEntitiesLoaded(); - }); - loadEntities.whenComplete((result, throwable) -> - CustomCrops.getInstance().getScheduler().runTask(() -> { - CustomCrops.getInstance().getWorldDataManager().removeCropData(cropLoc); - if (CustomCrops.getInstance().getPlatformInterface().removeCustomItem(bLoc, itemMode)) { - doTriggerActions(null, cropLoc, itemMode); - } - })); - } else { - asyncGetChunk.whenComplete((result, throwable) -> - CustomCrops.getInstance().getScheduler().runTask(() -> { - CustomCrops.getInstance().getWorldDataManager().removeCropData(cropLoc); - if (CustomCrops.getInstance().getPlatformInterface().removeCustomItem(bLoc, itemMode)) { - doTriggerActions(null, cropLoc, itemMode); - } - })); - } - } - } - - private void doTriggerActions(@Nullable Player player, @NotNull SimpleLocation crop_loc, ItemMode itemMode) { - if (triggerAction) { - StageConfig stageConfig = CustomCrops.getInstance().getCropManager().getStageConfig(stageID); - if (stageConfig != null) { - Action[] actions = stageConfig.getBreakActions(); - if (actions != null) { - for (Action action : actions) { - action.doOn(player, crop_loc, itemMode); - } - } - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ChainImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ChainImpl.java deleted file mode 100644 index bc8f7ed..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ChainImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.requirement.CurrentState; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public class ChainImpl implements Action { - - private final Action[] actions; - private final double chance; - private final Requirement[] requirements; - - public ChainImpl(Action[] actions, Requirement[] requirements, double chance) { - this.actions = actions; - this.requirements = requirements; - this.chance = chance; - } - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (Math.random() < chance) { - if (requirements != null && player != null) { - var state = new CurrentState(cropLoc == null ? player.getLocation() : cropLoc.getBukkitLocation(), player); - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(state)) { - return; - } - } - } - for (Action action : actions) { - action.doOn(player, cropLoc, itemMode); - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/CommandActionImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/CommandActionImpl.java deleted file mode 100644 index 381f117..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/CommandActionImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public record CommandActionImpl(String[] commands, double chance) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player == null || Math.random() > chance) return; - for (String command : commands) { - Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), - command.replace("{player}", player.getName()) - .replace("{x}", cropLoc == null ? "" : String.valueOf(cropLoc.getX())) - .replace("{y}", cropLoc == null ? "" : String.valueOf(cropLoc.getY())) - .replace("{z}", cropLoc == null ? "" : String.valueOf(cropLoc.getZ())) - .replace("{world}", player.getWorld().getName()) - ); - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/DropItemImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/DropItemImpl.java deleted file mode 100644 index bc4f441..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/DropItemImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.loot.Loot; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public record DropItemImpl(Loot[] loots, boolean toInv) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (cropLoc == null) return; - if (player != null) { - for (Loot loot : loots) { - loot.drop(player, cropLoc.getBukkitLocation(), toInv); - } - } else { - CustomCrops.getInstance().getScheduler().runTask(() -> { - for (Loot loot : loots) { - loot.drop(null, cropLoc.getBukkitLocation(), toInv); - } - }); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/GiveMoneyImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/GiveMoneyImpl.java deleted file mode 100644 index f050014..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/GiveMoneyImpl.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.integration.VaultHook; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public record GiveMoneyImpl(double money, double chance) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player != null && Math.random() < chance) { - VaultHook vaultHook = CustomCrops.getInstance().getIntegrationManager().getVault(); - if (vaultHook != null) { - vaultHook.getEconomy().depositPlayer(player, money); - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/JobXPImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/JobXPImpl.java deleted file mode 100644 index d246600..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/JobXPImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.integration.JobInterface; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public record JobXPImpl(double amount, double chance, @Nullable String job) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player == null || Math.random() > chance) return; - JobInterface jobInterface = CustomCrops.getInstance().getIntegrationManager().getJobInterface(); - if (jobInterface == null) return; - jobInterface.addXp(player, amount, job); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/MessageActionImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/MessageActionImpl.java deleted file mode 100644 index 75c4a56..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/MessageActionImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public record MessageActionImpl(String[] messages, double chance) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player == null || Math.random() > chance) return; - for (String message : messages) { - AdventureUtils.playerMessage(player, - message.replace("{player}", player.getName()) - .replace("{world}", player.getWorld().getName()) - .replace("{x}", cropLoc == null ? "" : String.valueOf(cropLoc.getX())) - .replace("{y}", cropLoc == null ? "" : String.valueOf(cropLoc.getY())) - .replace("{z}", cropLoc == null ? "" : String.valueOf(cropLoc.getZ())) - ); - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ParticleImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ParticleImpl.java deleted file mode 100644 index 05f216f..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ParticleImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.Location; -import org.bukkit.Particle; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public class ParticleImpl implements Action { - - private final Particle particle; - private final int amount; - private final double offset; - - public ParticleImpl(Particle particle, int amount, double offset) { - this.particle = particle; - this.amount = amount; - this.offset = offset; - } - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (cropLoc == null) return; - Location location = cropLoc.getBukkitLocation(); - if (location == null) return; - location.getWorld().spawnParticle(particle, location.clone().add(0.5,0.5,0.5), amount, offset, offset, offset); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/PotionEffectImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/PotionEffectImpl.java deleted file mode 100644 index 317dd87..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/PotionEffectImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffect; -import org.jetbrains.annotations.Nullable; - -public record PotionEffectImpl(PotionEffect potionEffect, double chance) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player == null || Math.random() > chance) return; - player.addPotionEffect(potionEffect); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ReplantImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ReplantImpl.java deleted file mode 100644 index 1177c9b..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/ReplantImpl.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.event.CropPlantEvent; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.api.object.crop.CropConfig; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.CompletableFuture; - -public class ReplantImpl implements Action { - - private final int point; - private final String crop; - private final String model; - - public ReplantImpl(int point, String model, String crop) { - this.point = point; - this.crop = crop; - this.model = model; - } - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (cropLoc == null) return; - CropConfig cropConfig = CustomCrops.getInstance().getCropManager().getCropConfigByID(crop); - if (cropConfig != null) { - Location location = cropLoc.getBukkitLocation(); - if (location == null) return; - ItemMode newCMode = cropConfig.getCropMode(); - if (ConfigManager.enableLimitation && CustomCrops.getInstance().getWorldDataManager().getChunkCropAmount(cropLoc) >= ConfigManager.maxCropPerChunk) { - AdventureUtils.playerMessage(player, MessageManager.prefix + MessageManager.reachChunkLimit); - return; - } - if (player != null) { - CropPlantEvent cropPlantEvent = new CropPlantEvent(player, player.getInventory().getItemInMainHand(), location, crop, point, model); - Bukkit.getPluginManager().callEvent(cropPlantEvent); - if (cropPlantEvent.isCancelled()) { - return; - } - if (!CustomCrops.getInstance().getPlatformInterface().detectAnyThing(location)) { - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, model, newCMode); - CustomCrops.getInstance().getWorldDataManager().addCropData(cropLoc, new GrowingCrop(crop, point), true); - } - } else { - CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); - if (itemMode == ItemMode.ITEM_FRAME || itemMode == ItemMode.ITEM_DISPLAY) { - CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { - chunk.getEntities(); - return chunk.isEntitiesLoaded(); - }); - loadEntities.whenComplete((result, throwable) -> - CustomCrops.getInstance().getScheduler().runTask(() -> { - if (!CustomCrops.getInstance().getPlatformInterface().detectAnyThing(location)) { - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, model, newCMode); - CustomCrops.getInstance().getWorldDataManager().addCropData(cropLoc, new GrowingCrop(crop, point), true); - } - })); - } else { - asyncGetChunk.whenComplete((result, throwable) -> - CustomCrops.getInstance().getScheduler().runTask(() -> { - if (!CustomCrops.getInstance().getPlatformInterface().detectAnyThing(location)) { - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, model, newCMode); - CustomCrops.getInstance().getWorldDataManager().addCropData(cropLoc, new GrowingCrop(crop, point), true); - } - })); - } - } - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SkillXPImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SkillXPImpl.java deleted file mode 100644 index cb7f11f..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SkillXPImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.integration.SkillInterface; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public record SkillXPImpl(double amount, double chance) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player == null || Math.random() > chance) return; - SkillInterface skillInterface = CustomCrops.getInstance().getIntegrationManager().getSkillInterface(); - if (skillInterface == null) return; - skillInterface.addXp(player, amount); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SoundActionImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SoundActionImpl.java deleted file mode 100644 index 49fafd6..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SoundActionImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.Locale; - -public record SoundActionImpl(String source, String sound, float volume, float pitch) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player == null) return; - AdventureUtils.playerSound(player, Sound.Source.valueOf(source.toUpperCase(Locale.ENGLISH)), Key.key(sound), volume, pitch); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SwingHandImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SwingHandImpl.java deleted file mode 100644 index 73c9146..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/SwingHandImpl.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public class SwingHandImpl implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player != null) { - player.swingMainHand(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/VanillaXPImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/VanillaXPImpl.java deleted file mode 100644 index 0200651..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/VanillaXPImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public record VanillaXPImpl(int amount, boolean mending, double chance) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (player == null || Math.random() > chance) return; - player.giveExp(amount, mending); - AdventureUtils.playerSound(player, Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/VariationImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/action/VariationImpl.java deleted file mode 100644 index a16cd37..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/VariationImpl.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.action; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.crop.VariationCrop; -import net.momirealms.customcrops.api.object.fertilizer.Variation; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.CompletableFuture; - -public record VariationImpl(VariationCrop[] variationCrops) implements Action { - - @Override - public void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode) { - if (cropLoc == null) return; - double bonus = 0; - Pot pot = CustomCrops.getInstance().getWorldDataManager().getPotData(cropLoc.add(0,-1,0)); - if (pot != null && CustomCrops.getInstance().getFertilizerManager().getConfigByFertilizer(pot.getFertilizer()) instanceof Variation variation) { - bonus = variation.getChance(); - } - for (VariationCrop variationCrop : variationCrops) { - if (Math.random() < variationCrop.getChance() + bonus) { - doVariation(cropLoc, itemMode, variationCrop); - break; - } - } - } - - public boolean doOn(@Nullable SimpleLocation crop_loc, ItemMode itemMode) { - if (crop_loc == null) return false; - double bonus = 0; - Pot pot = CustomCrops.getInstance().getWorldDataManager().getPotData(crop_loc.add(0,-1,0)); - if (pot != null && CustomCrops.getInstance().getFertilizerManager().getConfigByFertilizer(pot.getFertilizer()) instanceof Variation variation) { - bonus = variation.getChance(); - } - for (VariationCrop variationCrop : variationCrops) { - if (Math.random() < variationCrop.getChance() + bonus) { - doVariation(crop_loc, itemMode, variationCrop); - return true; - } - } - return false; - } - - private void doVariation(@NotNull SimpleLocation crop_loc, ItemMode itemMode, VariationCrop variationCrop) { - Location location = crop_loc.getBukkitLocation(); - if (location == null) return; - CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); - if (itemMode == ItemMode.ITEM_FRAME || itemMode == ItemMode.ITEM_DISPLAY) { - CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { - chunk.getEntities(); - return chunk.isEntitiesLoaded(); - }); - loadEntities.whenComplete((result, throwable) -> - CustomCrops.getInstance().getScheduler().callSyncMethod(() -> { - if (CustomCrops.getInstance().getPlatformInterface().removeCustomItem(location, itemMode)) { - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, variationCrop.getId(), variationCrop.getCropMode()); - } - return null; - })); - } else { - asyncGetChunk.whenComplete((result, throwable) -> - CustomCrops.getInstance().getScheduler().callSyncMethod(() -> { - if (CustomCrops.getInstance().getPlatformInterface().removeCustomItem(location, itemMode)) { - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, variationCrop.getId(), variationCrop.getCropMode()); - } - return null; - })); - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/basic/ConfigManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/basic/ConfigManager.java deleted file mode 100644 index 2a3ab5e..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/basic/ConfigManager.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.basic; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; - -public class ConfigManager extends Function { - - public static String lang; - public static boolean enableBStats; - public static boolean checkUpdate; - public static boolean enableSkillBonus; - public static String bonusFormula; - public static int greenhouseRange; - public static boolean whiteListWorlds; - public static HashSet worldList; - public static String worldFolderPath; - public static boolean debugScheduler; - public static boolean debugCorruption; - public static String greenhouseBlock; - public static String scarecrow; - public static boolean enableGreenhouse; - public static int pointGainInterval; - public static int corePoolSize; - public static double[] defaultRatio; - public static int maxPoolSize; - public static long keepAliveTime; - public static int seasonInterval; - public static boolean enableSeason; - public static boolean rsHook; - public static boolean enableScheduleSystem; - public static boolean syncSeason; - public static boolean autoSeasonChange; - public static String referenceWorld; - public static boolean enableLimitation; - public static int maxCropPerChunk; - public static int cacheSaveInterval; - public static int intervalConsume; - public static int intervalWork; - public static int fixRange; - public static boolean disableMoistureMechanic; - public static boolean preventTrampling; - public static boolean onlyInLoadedChunks; - public static boolean enableCorruptionFixer; - public static boolean debugWorld; - public static boolean updateDuringLoading; - - private final HashMap cropPerWorld; - private final CustomCrops plugin; - - public ConfigManager(CustomCrops plugin) { - this.plugin = plugin; - this.cropPerWorld = new HashMap<>(); - YamlConfiguration config = ConfigUtils.getConfig("config.yml"); - onlyInLoadedChunks = config.getBoolean("mechanics.only-work-in-loaded-chunks", false); - } - - @Override - public void load() { - this.loadConfig(); - } - - @Override - public void unload() { - this.cropPerWorld.clear(); - } - - private void loadConfig() { - if (new File(plugin.getDataFolder(), "config.yml").exists()) ConfigUtils.update("config.yml"); - YamlConfiguration config = ConfigUtils.getConfig("config.yml"); - enableBStats = config.getBoolean("metrics"); - lang = config.getString("lang"); - debugScheduler = config.getBoolean("debug.log-scheduler", false); - debugCorruption = config.getBoolean("debug.log-corruption-fixer", false); - debugWorld = config.getBoolean("debug.log-world-state", false); - loadWorlds(Objects.requireNonNull(config.getConfigurationSection("worlds"))); - loadScheduleSystem(Objects.requireNonNull(config.getConfigurationSection("schedule-system"))); - loadMechanic(Objects.requireNonNull(config.getConfigurationSection("mechanics"))); - loadOtherSetting(Objects.requireNonNull(config.getConfigurationSection("other-settings"))); - loadOptimization(Objects.requireNonNull(config.getConfigurationSection("optimization"))); - } - - private void loadOptimization(ConfigurationSection section) { - enableLimitation = section.getBoolean("limitation.growing-crop-amount.enable", true); - maxCropPerChunk = section.getInt("limitation.growing-crop-amount.default", 64); - updateDuringLoading = !ConfigManager.onlyInLoadedChunks && section.getBoolean("only-update-during-chunk-loading", false); - List worldSettings = section.getStringList("limitation.growing-crop-amount.worlds"); - for (String setting : worldSettings) { - String[] split = setting.split(":", 2); - try { - cropPerWorld.put(split[0], Integer.parseInt(split[1])); - } catch (NumberFormatException e) { - AdventureUtils.consoleMessage("[CustomCrops] Wrong number format found at: optimization.limitation.growing-crop-amount.worlds in config.yml"); - } - } - } - - private void loadWorlds(ConfigurationSection section) { - worldFolderPath = section.getString("absolute-world-folder-path", ""); - whiteListWorlds = section.getString("mode", "whitelist").equalsIgnoreCase("whitelist"); - worldList = new HashSet<>(section.getStringList("list")); - } - - private void loadScheduleSystem(ConfigurationSection section) { - enableScheduleSystem = section.getBoolean("enable", true); - pointGainInterval = section.getInt("point-gain-interval", 600); - corePoolSize = section.getInt("thread-pool-settings.corePoolSize", 2); - maxPoolSize = section.getInt("thread-pool-settings.maximumPoolSize", 4); - keepAliveTime = section.getInt("thread-pool-settings.keepAliveTime", 10); - cacheSaveInterval = section.getInt("cache-save-interval", 12000); - intervalConsume = section.getInt("consume-water-fertilizer-every-x-point", 2); - intervalWork = section.getInt("sprinkler-work-every-x-point", 2); - } - - private void loadMechanic(ConfigurationSection section) { - defaultRatio = ConfigUtils.getQualityRatio(section.getString("default-quality-ratio", "17/2/1")); - enableSeason = section.getBoolean("season.enable", true); - syncSeason = section.getBoolean("season.sync-season.enable", false); - referenceWorld = section.getString("season.sync-season.reference"); - autoSeasonChange = section.getBoolean("season.auto-season-change.enable"); - seasonInterval = section.getInt("season.auto-season-change.duration", 28); - enableGreenhouse = section.getBoolean("season.greenhouse.enable", true); - greenhouseRange = section.getInt("season.greenhouse.range", 5); - greenhouseBlock = section.getString("season.greenhouse.block"); - scarecrow = section.getString("scarecrow"); - disableMoistureMechanic = section.getBoolean("vanilla-farmland.disable-moisture-mechanic", false); - preventTrampling = section.getBoolean("vanilla-farmland.prevent-trampling", false); - } - - private void loadOtherSetting(ConfigurationSection section) { - enableSkillBonus = section.getBoolean("skill-bonus.enable", false); - bonusFormula = section.getString("skill-bonus.formula"); - enableCorruptionFixer = section.getBoolean("enable-corruption-fixer", true); - fixRange = section.getInt("corrupt-fix-range", 4); - } - - public int getCropLimit(String world) { - return Objects.requireNonNullElse(cropPerWorld.get(world), maxCropPerChunk); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/basic/MessageManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/basic/MessageManager.java deleted file mode 100644 index ea36924..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/basic/MessageManager.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.basic; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; - -public class MessageManager extends Function { - - private CustomCrops plugin; - - public static String prefix; - public static String reload; - public static String unavailableArgs; - public static String noConsole; - public static String notOnline; - public static String lackArgs; - public static String nonArgs; - public static String beforePlant; - public static String unsuitablePot; - public static String reachChunkLimit; - public static String spring; - public static String summer; - public static String autumn; - public static String winter; - public static String noPerm; - public static String noSeason; - public static String setSeason; - public static String setDate; - public static String worldNotExist; - public static String seasonNotExist; - public static String forceWork; - public static String forceConsume; - public static String forceGrow; - - public MessageManager(CustomCrops plugin) { - this.plugin =plugin; - } - - @Override - public void load() { - this.loadMessage(); - } - - private void loadMessage() { - YamlConfiguration config = ConfigUtils.getConfig("messages" + File.separator + "messages_" + ConfigManager.lang + ".yml"); - prefix = config.getString("messages.prefix","[CustomCrops] "); - reload = config.getString("messages.reload", "Reloaded! Took {time}ms."); - unavailableArgs = config.getString("messages.invalid-args", "Invalid arguments."); - noConsole = config.getString("messages.no-console", "This command can only be executed by a player."); - notOnline = config.getString("messages.not-online", "Player {player} is not online."); - lackArgs = config.getString("messages.lack-args", "Arguments are insufficient."); - nonArgs = config.getString("messages.not-none-args", "Not a none argument command."); - beforePlant = config.getString("messages.before-plant", "This fertilizer can only be used before planting."); - unsuitablePot = config.getString("messages.unsuitable-pot", "You can't plant the seed in this pot."); - reachChunkLimit = config.getString("messages.reach-crop-limit", "The number of crops has reached the limitation."); - noPerm = config.getString("messages.no-perm", "You don't have permission to do that."); - spring = config.getString("messages.spring", "Spring"); - summer = config.getString("messages.summer", "Summer"); - autumn = config.getString("messages.autumn", "Autumn"); - winter = config.getString("messages.winter", "Winter"); - noSeason = config.getString("messages.no-season", "SEASON DISABLED IN THIS WORLD"); - setSeason = config.getString("messages.set-season", "Successfully set {world}'s season to {season}."); - setDate = config.getString("messages.set-date", "Successfully set {world}'s date to {date}."); - worldNotExist = config.getString("messages.world-not-exist", "World {world} does not exist."); - seasonNotExist = config.getString("messages.season-not-exist", "Season {season} does not exist."); - forceWork = config.getString("messages.force-sprinkler-work", "Forced {world}'s sprinklers to work."); - forceConsume = config.getString("messages.force-consume", "Forced {world}'s pots to reduce water amount and the remaining use of fertilizers."); - forceGrow = config.getString("messages.force-grow", "Forced {world}'s crops to grow one point."); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/AndCondition.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/AndCondition.java deleted file mode 100644 index 9cbb49d..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/AndCondition.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.api.object.world.SimpleLocation; - -import java.util.List; - -public class AndCondition implements Condition { - - private final List deathConditions; - - public AndCondition(List deathConditions) { - this.deathConditions = deathConditions; - } - - @Override - public boolean isMet(SimpleLocation simpleLocation) { - for (Condition condition : deathConditions) { - if (!condition.isMet(simpleLocation)) { - return false; - } - } - return true; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/CrowAttack.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/CrowAttack.java deleted file mode 100644 index 1758a9a..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/CrowAttack.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.CrowTask; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Player; - -public class CrowAttack implements Condition { - - private final double chance; - private final String fly_model; - private final String stand_model; - - public CrowAttack(double chance, String fly_model, String stand_model) { - this.chance = chance; - this.fly_model = fly_model; - this.stand_model = stand_model; - } - - @Override - public boolean isMet(SimpleLocation simpleLocation) { - if (Math.random() > chance) return false; - if (CustomCrops.getInstance().getWorldDataManager().hasScarecrow(simpleLocation)) return false; - Location location = simpleLocation.getBukkitLocation(); - if (location == null) return false; - for (Player player : Bukkit.getOnlinePlayers()) { - SimpleLocation playerLoc = SimpleLocation.getByBukkitLocation(player.getLocation()); - if (playerLoc.isNear(simpleLocation, 48)) { - new CrowTask(player, location, fly_model, stand_model).runTaskTimerAsynchronously(CustomCrops.getInstance(), 1, 1); - } - } - return true; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/DeathCondition.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/DeathCondition.java deleted file mode 100644 index b144f40..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/DeathCondition.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.ItemType; -import net.momirealms.customcrops.api.object.OfflineReplaceTask; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.CompletableFuture; - -public class DeathCondition { - - private final String dead_model; - private final Condition[] conditions; - - public DeathCondition(@Nullable String dead_model, @NotNull Condition[] conditions) { - this.dead_model = dead_model; - this.conditions = conditions; - } - - public boolean checkIfDead(SimpleLocation simpleLocation) { - for (Condition condition : conditions) { - if (condition.isMet(simpleLocation)) { - return true; - } - } - return false; - } - - public void applyDeadModel(SimpleLocation simpleLocation, ItemMode itemMode) { - Location location = simpleLocation.getBukkitLocation(); - if (location == null) return; - - if (location.getWorld().isChunkLoaded(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4)) { - replaceDeadModels(location, itemMode); - return; - } - - if (ConfigManager.updateDuringLoading) { - CustomCrops.getInstance().getWorldDataManager().addOfflineTask(simpleLocation, new OfflineReplaceTask(dead_model, ItemType.CROP, itemMode)); - return; - } - - CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); - if (itemMode == ItemMode.ITEM_FRAME || itemMode == ItemMode.ITEM_DISPLAY) { - CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { - chunk.getEntities(); - return chunk.isEntitiesLoaded(); - }); - loadEntities.whenComplete((result, throwable) -> replaceDeadModels(location, itemMode)); - } else { - asyncGetChunk.whenComplete((result, throwable) -> replaceDeadModels(location, itemMode)); - } - } - - private void replaceDeadModels(Location location, ItemMode itemMode) { - CustomCrops.getInstance().getScheduler().runTask(() -> { - if (CustomCrops.getInstance().getPlatformInterface().removeCustomItem(location, itemMode)) { - if (dead_model != null) { - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(location, dead_model, itemMode); - } - } - }); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/OrCondition.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/OrCondition.java deleted file mode 100644 index 6730454..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/OrCondition.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.api.object.world.SimpleLocation; - -import java.util.List; - -public class OrCondition implements Condition { - - private final List deathConditions; - - public OrCondition(List deathConditions) { - this.deathConditions = deathConditions; - } - - @Override - public boolean isMet(SimpleLocation simpleLocation) { - for (Condition condition : deathConditions) { - if (condition.isMet(simpleLocation)) { - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/RightSeason.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/RightSeason.java deleted file mode 100644 index 61bee3a..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/RightSeason.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.api.object.world.WorldDataManager; - -public class RightSeason implements Condition { - - private final CCSeason[] seasons; - - public RightSeason(CCSeason[] seasons) { - this.seasons = seasons; - } - - @Override - public boolean isMet(SimpleLocation simpleLocation) { - String world = simpleLocation.getWorldName(); - CCSeason current = CustomCrops.getInstance().getIntegrationManager().getSeasonInterface().getSeason(world); - for (CCSeason allowed : seasons) { - if (current == allowed) { - return true; - } - } - WorldDataManager worldDataManager = CustomCrops.getInstance().getWorldDataManager(); - if (ConfigManager.enableGreenhouse) { - for (int i = 0; i < ConfigManager.greenhouseRange; i++) { - if (worldDataManager.isGreenhouse(simpleLocation.add(0, i, 0))) { - return true; - } - } - } - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WaterLessThan.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WaterLessThan.java deleted file mode 100644 index 1154c8b..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WaterLessThan.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.world.SimpleLocation; - -public class WaterLessThan implements Condition { - - private final int amount; - - public WaterLessThan(int amount) { - this.amount = amount; - } - - @Override - public boolean isMet(SimpleLocation crop_loc) { - Pot pot = CustomCrops.getInstance().getWorldDataManager().getPotData(crop_loc.add(0,-1,0)); - if (pot == null) return true; - return pot.getWater() < amount; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WaterMoreThan.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WaterMoreThan.java deleted file mode 100644 index 67c6874..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WaterMoreThan.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.world.SimpleLocation; - -public class WaterMoreThan implements Condition { - - private final int amount; - - public WaterMoreThan(int amount) { - this.amount = amount; - } - - @Override - public boolean isMet(SimpleLocation crop_loc) { - Pot pot = CustomCrops.getInstance().getWorldDataManager().getPotData(crop_loc.add(0,-1,0)); - if (pot == null) return false; - return pot.getWater() > amount; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Weather.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Weather.java deleted file mode 100644 index e6f2851..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Weather.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import org.bukkit.World; - -public class Weather implements Condition { - - private final String[] weathers; - - public Weather(String[] weathers) { - this.weathers = weathers; - } - - @Override - public boolean isMet(SimpleLocation simpleLocation) { - World world = simpleLocation.getBukkitWorld(); - if (world == null) return false; - String currentWeather; - if (world.isThundering()) currentWeather = "thunder"; - else if (world.isClearWeather()) currentWeather = "clear"; - else currentWeather = "rain"; - for (String weather : weathers) { - if (weather.equals(currentWeather)) { - return true; - } - } - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WrongSeason.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WrongSeason.java deleted file mode 100644 index 10c62fc..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/WrongSeason.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.condition; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.api.object.world.WorldDataManager; - -public class WrongSeason implements Condition { - - private final CCSeason[] seasons; - - public WrongSeason(CCSeason[] seasons) { - this.seasons = seasons; - } - - @Override - public boolean isMet(SimpleLocation simpleLocation) { - String world = simpleLocation.getWorldName(); - CCSeason current = CustomCrops.getInstance().getIntegrationManager().getSeasonInterface().getSeason(world); - for (CCSeason bad : seasons) { - if (current == bad) { - WorldDataManager worldDataManager = CustomCrops.getInstance().getWorldDataManager(); - if (ConfigManager.enableGreenhouse) { - for (int i = 0; i < ConfigManager.greenhouseRange; i++) { - if (worldDataManager.isGreenhouse(simpleLocation.add(0, i, 0))) { - return false; - } - } - } - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/CropConfig.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/CropConfig.java deleted file mode 100644 index 198e53c..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/CropConfig.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.crop; - -import net.momirealms.customcrops.api.object.BoneMeal; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.api.object.condition.Condition; -import net.momirealms.customcrops.api.object.condition.DeathCondition; -import net.momirealms.customcrops.api.object.requirement.CurrentState; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; - -public class CropConfig { - - private final String key; - private final ItemMode itemMode; - private final String[] bottom_blocks; - private final int max_points; - private final HashMap stageMap; - private final Requirement[] plantRequirements; - private final Requirement[] breakRequirements; - private final DeathCondition[] deathConditions; - private final Condition[] growConditions; - private final BoneMeal[] boneMeals; - private final Action[] plantActions; - private final boolean rotation; - - public CropConfig( - String key, - ItemMode itemMode, - int max_points, - String[] bottom_blocks, - Requirement[] plantRequirements, - Requirement[] breakRequirements, - DeathCondition[] deathConditions, - Condition[] growConditions, - HashMap stageMap, - BoneMeal[] boneMeals, - Action[] plantActions, - boolean rotation - ) { - this.key = key; - this.itemMode = itemMode; - this.deathConditions = deathConditions; - this.plantRequirements = plantRequirements; - this.breakRequirements = breakRequirements; - this.max_points = max_points; - this.bottom_blocks = bottom_blocks; - this.stageMap = stageMap; - this.growConditions = growConditions; - this.boneMeals = boneMeals; - this.plantActions = plantActions; - this.rotation = rotation; - } - - public String getKey() { - return key; - } - - @NotNull - public ItemMode getCropMode() { - return itemMode; - } - - @Nullable - public StageConfig getStageConfig(int stage) { - return stageMap.get(stage); - } - - @NotNull - public String[] getPotWhitelist() { - return bottom_blocks; - } - - public int getMaxPoints() { - return max_points; - } - - @Nullable - public Requirement[] getPlantRequirements() { - return plantRequirements; - } - - @Nullable - public Requirement[] getBreakRequirements() { - return breakRequirements; - } - - @Nullable - public DeathCondition[] getDeathConditions() { - return deathConditions; - } - - @Nullable - public Condition[] getGrowConditions() { - return growConditions; - } - - @Nullable - public BoneMeal[] getBoneMeals() { - return boneMeals; - } - - @Nullable - public Action[] getPlantActions() { - return plantActions; - } - - public boolean isRotationEnabled() { - return rotation; - } - - public boolean canPlant(Player player, Location location) { - if (plantRequirements == null) return true; - CurrentState currentState = new CurrentState(location, player); - for (Requirement requirement : plantRequirements) { - if (!requirement.isConditionMet(currentState)) { - return false; - } - } - return true; - } - - public boolean canBreak(Player player, Location location) { - if (breakRequirements == null) return true; - CurrentState currentState = new CurrentState(location, player); - for (Requirement requirement : breakRequirements) { - if (!requirement.isConditionMet(currentState)) { - return false; - } - } - return true; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/CropManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/CropManager.java deleted file mode 100644 index d5a1e2b..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/CropManager.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.crop; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.InteractCrop; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.condition.Condition; -import net.momirealms.customcrops.api.object.condition.DeathCondition; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import net.momirealms.customcrops.customplugin.Platform; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Item; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.ItemSpawnEvent; -import org.bukkit.plugin.Plugin; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; - -public class CropManager extends Function implements Listener { - - private final CustomCrops plugin; - private final HashMap stageToCrop; - private final HashMap seedToCropConfig; - private final HashMap cropConfigMap; - private final HashMap stageConfigMap; - private final HashSet deadCrops; - private boolean hasCheckedTripwire; - - public CropManager(CustomCrops plugin) { - this.plugin = plugin; - this.stageToCrop = new HashMap<>(); - this.cropConfigMap = new HashMap<>(); - this.stageConfigMap = new HashMap<>(); - this.seedToCropConfig = new HashMap<>(); - this.deadCrops = new HashSet<>(); - } - - @Override - public void load() { - this.loadConfig(); - Bukkit.getPluginManager().registerEvents(this, plugin); - } - - @Override - public void unload() { - this.stageToCrop.clear(); - this.cropConfigMap.clear(); - this.stageConfigMap.clear(); - this.deadCrops.clear(); - this.seedToCropConfig.clear(); - HandlerList.unregisterAll(this); - } - - private void loadConfig() { - File crop_folder = new File(plugin.getDataFolder(), "contents" + File.separator + "crops"); - if (!crop_folder.exists()) { - if (!crop_folder.mkdirs()) return; - ConfigUtils.getConfig("contents" + File.separator + "crops" + File.separator + "tomato.yml"); - } - File[] files = crop_folder.listFiles(); - if (files == null) return; - for (File file : files) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (String key : config.getKeys(false)) { - ConfigurationSection cropSec = config.getConfigurationSection(key); - if (cropSec == null) continue; - ItemMode itemMode = ItemMode.valueOf(cropSec.getString("type", "TripWire").toUpperCase(Locale.ENGLISH)); - if (itemMode == ItemMode.TRIPWIRE && !hasCheckedTripwire) { - checkTripwire(); - } - String[] bottomBlocks = cropSec.getStringList("pot-whitelist").toArray(new String[0]); - if (bottomBlocks.length == 0) { - AdventureUtils.consoleMessage("[CustomCrops] pot-whitelist is not set for crop: " + key); - continue; - } - - String seed = cropSec.getString("seed"); - Requirement[] breakReq = ConfigUtils.getRequirementsWithMsg(cropSec.getConfigurationSection("requirements.break")); - Requirement[] plantReq = ConfigUtils.getRequirementsWithMsg(cropSec.getConfigurationSection("requirements.plant")); - - int max = cropSec.getInt("max-points", 0); - ConfigurationSection pointSec = cropSec.getConfigurationSection("points"); - if (pointSec == null || max == 0) { - AdventureUtils.consoleMessage("[CustomCrops] Points are not set for crop: " + key); - continue; - } - HashMap stageMap = new HashMap<>(); - for (String point : pointSec.getKeys(false)) { - try { - int parsed = Integer.parseInt(point); - String stageModel = pointSec.getString(point + ".model"); - StageConfig stageConfig = new StageConfig( - parsed, - stageModel, - ConfigUtils.getActions(pointSec.getConfigurationSection(point + ".events.break"), stageModel), - ConfigUtils.getActions(pointSec.getConfigurationSection(point + ".events.grow"), stageModel), - ConfigUtils.getInteractActions(pointSec.getConfigurationSection(point + ".events.interact-with-item"), stageModel), - pointSec.contains(point + ".events.interact-by-hand") ? new InteractCrop( - "AIR", - false, - null, - ConfigUtils.getActions(pointSec.getConfigurationSection(point + ".events.interact-by-hand"), stageModel), - ConfigUtils.getRequirementsWithMsg(pointSec.getConfigurationSection(point + ".events.interact-by-hand.requirements")) - ) : null, - pointSec.getDouble(point + ".hologram-offset-correction", 0d) - ); - stageMap.put(parsed, stageConfig); - if (stageModel != null) { - stageToCrop.put(stageModel, key); - stageConfigMap.put(stageModel, stageConfig); - } - } - catch (NumberFormatException e) { - AdventureUtils.consoleMessage("[CustomCrops] Unexpected point value: " + point); - } - } - DeathCondition[] deathConditions = ConfigUtils.getDeathConditions(cropSec.getConfigurationSection("death-conditions")); - Condition[] growConditions = ConfigUtils.getConditions(cropSec.getConfigurationSection("grow-conditions")); - CropConfig cropConfig = new CropConfig( - key, - itemMode, - max, - bottomBlocks, - plantReq, - breakReq, - deathConditions, - growConditions, - stageMap, - ConfigUtils.getBoneMeals(cropSec.getConfigurationSection("custom-bone-meal")), - ConfigUtils.getActions(cropSec.getConfigurationSection("plant-actions"), null), - cropSec.getBoolean("random-rotation", false) - ); - cropConfigMap.put(key, cropConfig); - if (seed != null) seedToCropConfig.put(seed, cropConfig); - } - } - AdventureUtils.consoleMessage("[CustomCrops] Loaded " + cropConfigMap.size() + " crop(s)"); - } - - @Nullable - public StageConfig getStageConfig(String stage_id) { - return stageConfigMap.get(stage_id); - } - - @Nullable - public CropConfig getCropConfigByID(String id) { - return this.cropConfigMap.get(id); - } - - @Nullable - public CropConfig getCropConfigByStage(String stage_id) { - String key = getCropConfigID(stage_id); - if (key == null) return null; - return this.cropConfigMap.get(key); - } - - @Nullable - public String getCropConfigID(String stage_id) { - return this.stageToCrop.get(stage_id); - } - - public boolean isDeadCrop(String id) { - return deadCrops.contains(id); - } - - public boolean containsStage(String stage_id) { - return stageToCrop.containsKey(stage_id); - } - - // Prevent players from getting stage model - @EventHandler - public void onItemSpawn(ItemSpawnEvent event) { - if (event.isCancelled()) return; - Item item = event.getEntity(); - String id = plugin.getPlatformInterface().getItemStackID(item.getItemStack()); - if (containsStage(id) || isDeadCrop(id)) { - event.setCancelled(true); - } - } - - public void registerDeadCrops(String id) { - this.deadCrops.add(id); - } - - @Nullable - public CropConfig getCropConfigBySeed(String seed) { - return seedToCropConfig.get(seed); - } - - private void checkTripwire() { - hasCheckedTripwire = true; - if (plugin.getPlatform() == Platform.ItemsAdder) { - Plugin iaP = Bukkit.getPluginManager().getPlugin("ItemsAdder"); - if (iaP != null) { - FileConfiguration config = iaP.getConfig(); - boolean disabled = config.getBoolean("blocks.disable-REAL_WIRE"); - if (disabled) { - AdventureUtils.consoleMessage("========================[CustomCrops]========================="); - AdventureUtils.consoleMessage(" Detected that one of your crops is using TRIPWIRE type"); - AdventureUtils.consoleMessage(" If you want to use tripwire for custom crops, please set"); - AdventureUtils.consoleMessage("\"blocks.disable-REAL_WIRE: false\" in /ItemsAdder/config.yml"); - AdventureUtils.consoleMessage(" Change this setting requires a server restart"); - AdventureUtils.consoleMessage(" If you have problems with which one to use, read the wiki."); - AdventureUtils.consoleMessage("=============================================================="); - } - } - } else if (plugin.getPlatform() == Platform.Oraxen) { - Plugin oxP = Bukkit.getPluginManager().getPlugin("Oraxen"); - if (oxP != null) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(new File(oxP.getDataFolder(), "mechanics.yml")); - boolean disabled = !config.getBoolean("stringblock.enabled"); - if (disabled) { - AdventureUtils.consoleMessage("========================[CustomCrops]========================="); - AdventureUtils.consoleMessage(" Detected that one of your crops is using TRIPWIRE type"); - AdventureUtils.consoleMessage(" If you want to use tripwire for custom crops, please set"); - AdventureUtils.consoleMessage(" \"stringblock.enabled: true\" in /Oraxen/mechanics.yml"); - AdventureUtils.consoleMessage(" If you have problems with which one to use, read the wiki."); - AdventureUtils.consoleMessage("=============================================================="); - } - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/GrowingCrop.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/GrowingCrop.java deleted file mode 100644 index 0e00018..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/GrowingCrop.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.crop; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.CCGrowingCrop; - -import java.io.Serial; -import java.io.Serializable; - -public class GrowingCrop implements Serializable, CCGrowingCrop { - - @Serial - private static final long serialVersionUID = 2828962866548871991L; - - private int points; - private final String crop; - - public GrowingCrop(String crop, int points) { - this.points = points; - this.crop = crop; - } - - public int getPoints() { - return points; - } - - public void setPoints(int points) { - this.points = points; - } - - public String getKey() { - return crop; - } - - public CropConfig getConfig() { - return CustomCrops.getInstance().getCropManager().getCropConfigByID(crop); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/StageConfig.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/StageConfig.java deleted file mode 100644 index 7df283e..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/StageConfig.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.crop; - -import net.momirealms.customcrops.api.object.InteractCrop; -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -public class StageConfig { - - private final int point; - private final String model; - private final Action[] breakActions; - private final InteractCrop[] interactWithItem; - private final Action[] growActions; - private final InteractCrop interactByHand; - private final double offsetCorrection; - - public StageConfig(int point, @Nullable String model, @Nullable Action[] breakActions, @Nullable Action[] growActions, @Nullable InteractCrop[] interactWithItem, @Nullable InteractCrop interactByHand, double offsetCorrection) { - this.point = point; - this.breakActions = breakActions; - this.interactWithItem = interactWithItem; - this.growActions = growActions; - this.interactByHand = interactByHand; - this.model = model; - this.offsetCorrection = offsetCorrection; - } - - @Nullable - public Action[] getBreakActions() { - return breakActions; - } - - @Nullable - public InteractCrop[] getInteractCropWithItem() { - return interactWithItem; - } - - @Nullable - public Action[] getGrowActions() { - return growActions; - } - - @Nullable - public InteractCrop getInteractByHand() { - return interactByHand; - } - - @Nullable - public String getModel() { - return model; - } - - public double getOffsetCorrection() { - return offsetCorrection; - } - - public int getPoint() { - return point; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Fertilizer.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Fertilizer.java deleted file mode 100644 index 9a9293b..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Fertilizer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.fertilizer; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.CCFertilizer; - -import java.io.Serializable; - -public class Fertilizer implements Serializable, CCFertilizer { - - private final String key; - private int times; - - public Fertilizer(String key, int times) { - this.key = key; - this.times = times; - } - - public Fertilizer(FertilizerConfig fertilizerConfig) { - this.key = fertilizerConfig.getKey(); - this.times = fertilizerConfig.getTimes(); - } - - /* - If fertilizer is used up - */ - public boolean reduceTimes() { - times--; - return times <= 0; - } - - public String getKey() { - return key; - } - - public FertilizerConfig getConfig() { - return CustomCrops.getInstance().getFertilizerManager().getConfigByFertilizer(this); - } - - public int getLeftTimes() { - return times; - } - - public void setTimes(int times) { - this.times = times; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerConfig.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerConfig.java deleted file mode 100644 index d54196f..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerConfig.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.fertilizer; - -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.requirement.CurrentState; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.bukkit.Location; -import org.bukkit.Particle; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public abstract class FertilizerConfig { - - private final int times; - private final double chance; - private final String key; - private final FertilizerType fertilizerType; - private final String[] pot_whitelist; - private final boolean beforePlant; - private final Particle particle; - private final Sound sound; - private final String icon; - private final Requirement[] requirements; - - public FertilizerConfig( - String key, - FertilizerType fertilizerType, - int times, - double chance, - @Nullable String[] pot_whitelist, - boolean beforePlant, - @Nullable Particle particle, - @Nullable Sound sound, - @Nullable String icon, - Requirement[] requirements - ) { - this.times = times; - this.chance = chance; - this.key = key; - this.fertilizerType = fertilizerType; - this.pot_whitelist = pot_whitelist; - this.beforePlant = beforePlant; - this.particle = particle; - this.sound = sound; - this.icon = icon; - this.requirements = requirements; - } - - public int getTimes() { - return times; - } - - public double getChance() { - return chance; - } - - public boolean canTakeEffect() { - return Math.random() < chance; - } - - public String getKey() { - return key; - } - - public FertilizerType getFertilizerType() { - return fertilizerType; - } - - @Nullable - public String[] getPotWhitelist() { - return pot_whitelist; - } - - public boolean isBeforePlant() { - return beforePlant; - } - - @Nullable - public Particle getParticle() { - return particle; - } - - @Nullable - public Sound getSound() { - return sound; - } - - public String getIcon() { - return icon; - } - - public Requirement[] getRequirements() { - return requirements; - } - - public boolean canUse(Player player, Location location) { - if (requirements == null) return true; - CurrentState currentState = new CurrentState(location, player); - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(currentState)) { - return false; - } - } - return true; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerManager.java deleted file mode 100644 index b3da0cc..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/FertilizerManager.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.fertilizer; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.Pair; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Particle; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.intellij.lang.annotations.Subst; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Locale; - -public class FertilizerManager extends Function { - - private final CustomCrops plugin; - private final HashMap fertilizerConfigMap; - private final HashMap itemToKey; - - public FertilizerManager(CustomCrops plugin) { - this.plugin = plugin; - this.fertilizerConfigMap = new HashMap<>(); - this.itemToKey = new HashMap<>(); - } - - @Override - public void load() { - this.loadConfig(); - } - - @Override - public void unload() { - this.fertilizerConfigMap.clear(); - this.itemToKey.clear(); - } - - @Nullable - public FertilizerConfig getConfigByFertilizer(@Nullable Fertilizer fertilizer) { - if (fertilizer == null) return null; - return fertilizerConfigMap.get(fertilizer.getKey()); - } - - @Nullable - public FertilizerConfig getConfigByKey(String key) { - return fertilizerConfigMap.get(key); - } - - @Nullable - public FertilizerConfig getConfigByItemID(String id) { - String key = itemToKey.get(id); - if (key == null) return null; - return getConfigByKey(key); - } - - private void loadConfig() { - File can_folder = new File(plugin.getDataFolder(), "contents" + File.separator + "fertilizers"); - if (!can_folder.exists()) { - if (!can_folder.mkdirs()) return; - ConfigUtils.getConfig("contents" + File.separator + "fertilizers" + File.separator + "speed-grow.yml"); - ConfigUtils.getConfig("contents" + File.separator + "fertilizers" + File.separator + "quality.yml"); - ConfigUtils.getConfig("contents" + File.separator + "fertilizers" + File.separator + "soil-retain.yml"); - ConfigUtils.getConfig("contents" + File.separator + "fertilizers" + File.separator + "yield-increase.yml"); - ConfigUtils.getConfig("contents" + File.separator + "fertilizers" + File.separator + "variation.yml"); - } - File[] files = can_folder.listFiles(); - if (files == null) return; - for (File file : files) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (String key : config.getKeys(false)) { - ConfigurationSection fertilizerSec = config.getConfigurationSection(key); - if (fertilizerSec == null) continue; - FertilizerConfig fertilizerConfig; - FertilizerType fertilizerType = FertilizerType.valueOf(fertilizerSec.getString("type", "SPEED_GROW").toUpperCase(Locale.ENGLISH)); - String[] pot_whitelist = fertilizerSec.contains("pot-whitelist") ? fertilizerSec.getStringList("pot-whitelist").toArray(new String[0]) : null; - boolean beforePlant = fertilizerSec.getBoolean("before-plant", false); - int times = fertilizerSec.getInt("times", 14); - Particle particle = fertilizerSec.contains("particle") ? Particle.valueOf(fertilizerSec.getString("particle")) : null; - @Subst("namespace:key") String soundKey = fertilizerSec.getString("sound", "minecraft:item.hoe.till"); - Sound sound = fertilizerSec.contains("sound") ? Sound.sound(Key.key(soundKey), Sound.Source.PLAYER, 1, 1) : null; - String icon = fertilizerSec.getString("icon"); - Requirement[] requirements = ConfigUtils.getRequirementsWithMsg(fertilizerSec.getConfigurationSection("requirements")); - switch (fertilizerType) { - case SPEED_GROW -> fertilizerConfig = new SpeedGrow(key, fertilizerType, times, getChancePair(fertilizerSec), pot_whitelist, beforePlant, particle, sound, icon, requirements); - case YIELD_INCREASE -> fertilizerConfig = new YieldIncrease(key, fertilizerType, times, fertilizerSec.getDouble("chance"), getChancePair(fertilizerSec), pot_whitelist, beforePlant, particle, sound, icon, requirements); - case VARIATION -> fertilizerConfig = new Variation(key, fertilizerType, times, fertilizerSec.getDouble("chance"), pot_whitelist, beforePlant, particle, sound, icon, requirements); - case QUALITY -> fertilizerConfig = new Quality(key, fertilizerType, times, fertilizerSec.getDouble("chance"), ConfigUtils.getQualityRatio(fertilizerSec.getString("ratio", "2/2/1")), pot_whitelist, beforePlant, particle, sound, icon, requirements); - case SOIL_RETAIN -> fertilizerConfig = new SoilRetain(key, fertilizerType, times, fertilizerSec.getDouble("chance"), pot_whitelist, beforePlant, particle, sound, icon, requirements); - default -> fertilizerConfig = null; - } - String item = fertilizerSec.getString("item"); - if (fertilizerConfig != null && item != null) { - fertilizerConfigMap.put(key, fertilizerConfig); - itemToKey.put(item, key); - } - else - AdventureUtils.consoleMessage("[CustomCrops] Invalid fertilizer: " + key); - } - } - AdventureUtils.consoleMessage("[CustomCrops] Loaded " + fertilizerConfigMap.size() + " fertilizer(s)"); - } - - public ArrayList> getChancePair(ConfigurationSection fertilizerSec) { - ArrayList> pairs = new ArrayList<>(); - ConfigurationSection effectSec = fertilizerSec.getConfigurationSection("chance"); - if (effectSec == null) return new ArrayList<>(); - for (String point : effectSec.getKeys(false)) { - Pair pair = new Pair<>(effectSec.getDouble(point), Integer.parseInt(point)); - pairs.add(pair); - } - return pairs; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Quality.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Quality.java deleted file mode 100644 index 3ab9740..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Quality.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.fertilizer; - -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Particle; -import org.jetbrains.annotations.Nullable; - -public class Quality extends FertilizerConfig { - - private final double[] ratio; - - public Quality( - String key, - FertilizerType fertilizerType, - int times, - double chance, - double[] ratio, - @Nullable String[] pot_whitelist, - boolean beforePlant, - @Nullable Particle particle, - @Nullable Sound sound, - @Nullable String icon, - Requirement[] requirements - ) { - super(key, fertilizerType, times, chance, pot_whitelist, beforePlant, particle, sound, icon, requirements); - this.ratio = ratio; - if (this.ratio.length != ConfigManager.defaultRatio.length) { - AdventureUtils.consoleMessage("[CustomCrops] Wrong format found at fertilizer: " + key + ". You should make sure that all the quality ratio are in the same format. For example when you set default-ratio to x/x/x/x/x in config.yml. You should set ratio to x/x/x/x/x in fertilizers to work"); - } - } - - public double[] getRatio() { - return ratio; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/SoilRetain.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/SoilRetain.java deleted file mode 100644 index 608382e..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/SoilRetain.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.fertilizer; - -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.bukkit.Particle; -import org.jetbrains.annotations.Nullable; - -public class SoilRetain extends FertilizerConfig { - - public SoilRetain( - String key, - FertilizerType fertilizerType, - int times, - double chance, - @Nullable String[] pot_whitelist, - boolean beforePlant, - @Nullable Particle particle, - @Nullable Sound sound, - String icon, - Requirement[] requirements - ) { - super(key, fertilizerType, times, chance, pot_whitelist, beforePlant, particle, sound, icon, requirements); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Variation.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Variation.java deleted file mode 100644 index cc515db..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/Variation.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.fertilizer; - -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.bukkit.Particle; -import org.jetbrains.annotations.Nullable; - -public class Variation extends FertilizerConfig { - - public Variation( - String key, - FertilizerType fertilizerType, - int times, - double chance, - @Nullable String[] pot_whitelist, - boolean beforePlant, - @Nullable Particle particle, - @Nullable Sound sound, - String icon, - Requirement[] requirements - ) { - super(key, fertilizerType, times, chance, pot_whitelist, beforePlant, particle, sound, icon, requirements); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/AbstractHologram.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/AbstractHologram.java deleted file mode 100644 index 003681f..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/AbstractHologram.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.hologram; - -public abstract class AbstractHologram { - - protected final String content; - private final double offset; - private final HologramManager.Mode mode; - private final int duration; - private TextDisplayMeta textDisplayMeta; - - public AbstractHologram(String content, double offset, HologramManager.Mode mode, int duration, TextDisplayMeta textDisplayMeta) { - this.content = content; - this.offset = offset; - this.mode = mode; - this.duration = duration; - this.textDisplayMeta = textDisplayMeta; - } - - public String getContent() { - return content; - } - - public double getOffset() { - return offset; - } - - public HologramManager.Mode getMode() { - return mode; - } - - public int getDuration() { - return duration; - } - - public TextDisplayMeta getTextDisplayMeta() { - return textDisplayMeta; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/FertilizerHologram.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/FertilizerHologram.java deleted file mode 100644 index 7a884ea..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/FertilizerHologram.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.hologram; - - -import net.momirealms.customcrops.api.object.CCFertilizer; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerConfig; -import org.jetbrains.annotations.NotNull; - -public class FertilizerHologram extends AbstractHologram { - - public FertilizerHologram(@NotNull String content, double offset, HologramManager.Mode mode, int duration, TextDisplayMeta textDisplayMeta) { - super(content, offset, mode, duration, textDisplayMeta); - } - - public String getContent(CCFertilizer CCFertilizer) { - Fertilizer fertilizer = (Fertilizer) CCFertilizer; - FertilizerConfig fertilizerConfig = fertilizer.getConfig(); - return content.replace("{icon}", String.valueOf(fertilizerConfig.getIcon())) - .replace("{left_times}", String.valueOf(fertilizer.getLeftTimes())) - .replace("{max_times}", String.valueOf(fertilizerConfig.getTimes())); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/TextDisplayMeta.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/TextDisplayMeta.java deleted file mode 100644 index e5796a1..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/TextDisplayMeta.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.momirealms.customcrops.api.object.hologram; - -public record TextDisplayMeta(boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackground, - int backgroundColor, byte opacity) { - -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/WaterAmountHologram.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/WaterAmountHologram.java deleted file mode 100644 index 67b7d2e..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/WaterAmountHologram.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.hologram; - -import org.jetbrains.annotations.NotNull; - -public class WaterAmountHologram extends AbstractHologram { - - private final String bar_left; - private final String bar_full; - private final String bar_empty; - private final String bar_right; - - - public WaterAmountHologram(@NotNull String content, double offset, HologramManager.Mode mode, int duration, - String bar_left, String bar_full, String bar_empty, String bar_right, TextDisplayMeta textDisplayMeta) { - super(content, offset, mode, duration, textDisplayMeta); - this.bar_left = bar_left; - this.bar_full = bar_full; - this.bar_empty = bar_empty; - this.bar_right = bar_right; - } - - public String getContent(int current, int storage) { - return super.content.replace("{current}", String.valueOf(current)) - .replace("{storage}", String.valueOf(storage)) - .replace("{water_bar}", getWaterBar(current, storage)); - } - - private String getWaterBar(int current, int storage) { - return bar_left + - String.valueOf(bar_full).repeat(current) + - String.valueOf(bar_empty).repeat(Math.max(storage - current, 0)) + - bar_right; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/Loot.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/Loot.java deleted file mode 100644 index 3d61065..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/Loot.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.loot; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.integration.SkillInterface; -import net.objecthunter.exp4j.Expression; -import net.objecthunter.exp4j.ExpressionBuilder; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.ThreadLocalRandom; - -public abstract class Loot { - - public int min; - public int max; - - public Loot(int min, int max) { - this.min = min; - this.max = max; - } - - public abstract void drop(@Nullable Player player, Location location, boolean toInv); - - public int getMin() { - return min; - } - - public int getMax() { - return max; - } - - public int getAmount(@Nullable Player player) { - int random = ThreadLocalRandom.current().nextInt(getMin(), getMax() + 1); - if (ConfigManager.enableSkillBonus && player != null) { - SkillInterface skillInterface = CustomCrops.getInstance().getIntegrationManager().getSkillInterface(); - if (skillInterface != null) { - int level = skillInterface.getLevel(player); - Expression expression = new ExpressionBuilder(ConfigManager.bonusFormula) - .variables("base", "level") - .build() - .setVariable("base", random) - .setVariable("level", level); - random = (int) expression.evaluate(); - } - } - return random; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/OtherLoot.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/OtherLoot.java deleted file mode 100644 index 84520a1..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/OtherLoot.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.loot; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.fertilizer.YieldIncrease; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.ItemUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -public class OtherLoot extends Loot { - - private final String itemID; - private final double chance; - - public OtherLoot(int min, int max, String itemID, double chance) { - super(min, max); - this.itemID = itemID; - this.chance = chance; - } - - public String getItemID() { - return itemID; - } - - public double getChance() { - return chance; - } - - @Override - public void drop(Player player, Location location, boolean toInv) { - if (Math.random() < getChance()) { - int random = getAmount(player); - Pot pot = CustomCrops.getInstance().getWorldDataManager().getPotData(SimpleLocation.getByBukkitLocation(location).add(0,-1,0)); - if (pot != null && pot.getFertilizer() != null && pot.getFertilizer().getConfig() instanceof YieldIncrease increase) { - random += increase.getAmountBonus(); - } - ItemStack drop = CustomCrops.getInstance().getIntegrationManager().build(getItemID(), player); - if (drop.getType() == Material.AIR) return; - drop.setAmount(random); - - if (toInv) { - int remain = ItemUtils.putLootsToBag(player.getInventory(), drop, drop.getAmount()); - if (remain > 0) { - drop.setAmount(remain); - location.getWorld().dropItemNaturally(location, drop); - } - } else { - location.getWorld().dropItemNaturally(location, drop); - } - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/QualityLoot.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/QualityLoot.java deleted file mode 100644 index e7638ac..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/loot/QualityLoot.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.loot; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.object.fertilizer.Quality; -import net.momirealms.customcrops.api.object.fertilizer.YieldIncrease; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.ItemUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -public class QualityLoot extends Loot { - - private final String[] qualityLoots; - - public QualityLoot(int min, int max, String... qualityLoots) { - super(min, max); - this.qualityLoots = qualityLoots; - } - - @Override - public void drop(Player player, Location location, boolean toInv) { - SimpleLocation simpleLocation = SimpleLocation.getByBukkitLocation(location); - Pot pot = CustomCrops.getInstance().getWorldDataManager().getPotData(simpleLocation.add(0,-1,0)); - int amount = getAmount(player); - double[] qualityRatio = ConfigManager.defaultRatio; - if (pot != null) { - FertilizerConfig fertilizerConfig = CustomCrops.getInstance().getFertilizerManager().getConfigByFertilizer(pot.getFertilizer()); - if (fertilizerConfig instanceof Quality quality && quality.canTakeEffect()) { - qualityRatio = quality.getRatio(); - } else if (fertilizerConfig instanceof YieldIncrease increase) { - amount += increase.getAmountBonus(); - } - } - for (int i = 0; i < amount; i++) { - double random = Math.random(); - for (int j = 0; j < qualityRatio.length; j++) { - if (random < qualityRatio[j]) { - dropItem(location, qualityLoots[j], player, toInv); - break; - } - } - } - } - - private void dropItem(Location location, String id, Player player, boolean toInv) { - ItemStack drop = CustomCrops.getInstance().getIntegrationManager().build(id, player); - if (drop.getType() == Material.AIR) return; - if (toInv) { - int remain = ItemUtils.putLootsToBag(player.getInventory(), drop, drop.getAmount()); - if (remain > 0) { - drop.setAmount(remain); - location.getWorld().dropItemNaturally(location, drop); - } - } else { - location.getWorld().dropItemNaturally(location, drop); - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/migrate/MigrateWorld.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/migrate/MigrateWorld.java deleted file mode 100644 index ea1e35b..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/migrate/MigrateWorld.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.migrate; - -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.sprinkler.Sprinkler; -import net.momirealms.customcrops.api.object.world.CCChunk; -import net.momirealms.customcrops.api.object.world.ChunkCoordinate; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.ConfigUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class MigrateWorld extends Function { - - private final String worldName; - private final ConcurrentHashMap chunkMap; - - public MigrateWorld(String world) { - this.worldName = world; - this.chunkMap = new ConcurrentHashMap<>(64); - } - - @Override - @SuppressWarnings("ResultOfMethodCallIgnored") - public void init() { - File chunks_folder = ConfigUtils.getFile(worldName, "chunks"); - if (!chunks_folder.exists()) chunks_folder.mkdirs(); - File[] data_files = chunks_folder.listFiles(); - if (data_files == null) return; - for (File file : data_files) { - ChunkCoordinate chunkCoordinate = ChunkCoordinate.getByString(file.getName().substring(0, file.getName().length() - 7)); - try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) { - CCChunk chunk = (CCChunk) ois.readObject(); - if (chunk.isUseless()) { - file.delete(); - continue; - } - if (chunkCoordinate != null) chunkMap.put(chunkCoordinate, chunk); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - } - } - } - - @Override - @SuppressWarnings("ResultOfMethodCallIgnored") - public void disable() { - File chunks_folder = ConfigUtils.getFile(worldName, "chunks"); - if (!chunks_folder.exists()) chunks_folder.mkdirs(); - for (Map.Entry entry : chunkMap.entrySet()) { - ChunkCoordinate chunkCoordinate = entry.getKey(); - CCChunk chunk = entry.getValue(); - String fileName = chunkCoordinate.getFileName() + ".ccdata"; - File file = new File(chunks_folder, fileName); - if (chunk.isUseless() && file.exists()) { - file.delete(); - continue; - } - try (FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos)) { - oos.writeObject(chunk); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - public String getWorldName() { - return worldName; - } - - public void removePotData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removePotData(simpleLocation); - } - - public void removeCropData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removeCropData(simpleLocation); - } - - public void addCropData(SimpleLocation simpleLocation, GrowingCrop growingCrop) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addCropData(simpleLocation, growingCrop); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addCropData(simpleLocation, growingCrop); - } - - public GrowingCrop getCropData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - return chunk.getCropData(simpleLocation); - } - return null; - } - - public int getChunkCropAmount(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return 0; - return chunk.getCropAmount(); - } - - public void removeGreenhouse(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removeGreenhouse(simpleLocation); - } - - public void addGreenhouse(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addGreenhouse(simpleLocation); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addGreenhouse(simpleLocation); - } - - public boolean isGreenhouse(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return false; - return chunk.isGreenhouse(simpleLocation); - } - - public void removeScarecrow(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removeScarecrow(simpleLocation); - } - - public void addScarecrow(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addScarecrow(simpleLocation); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addScarecrow(simpleLocation); - } - - public boolean hasScarecrow(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return false; - return chunk.hasScarecrow(); - } - - public void removeSprinklerData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removeSprinklerData(simpleLocation); - } - - public void addSprinklerData(SimpleLocation simpleLocation, Sprinkler sprinkler) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addSprinklerData(simpleLocation, sprinkler); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addSprinklerData(simpleLocation, sprinkler); - } - - @Nullable - public Sprinkler getSprinklerData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return null; - return chunk.getSprinklerData(simpleLocation); - } - - public void addWaterToPot(SimpleLocation simpleLocation, int amount, @NotNull String pot_id) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addWaterToPot(simpleLocation, amount, pot_id); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addWaterToPot(simpleLocation, amount, pot_id); - } - - public void addFertilizerToPot(SimpleLocation simpleLocation, Fertilizer fertilizer, @NotNull String pot_id) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addFertilizerToPot(simpleLocation, fertilizer, pot_id); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addFertilizerToPot(simpleLocation, fertilizer, pot_id); - } - - public Pot getPotData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return null; - return chunk.getPotData(simpleLocation); - } - - public void addPotData(SimpleLocation simpleLocation, Pot pot) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addPotData(simpleLocation, pot); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addPotData(simpleLocation, pot); - } - - public CCChunk createNewChunk(SimpleLocation simpleLocation) { - CCChunk newChunk = new CCChunk(); - chunkMap.put(simpleLocation.getChunkCoordinate(), newChunk); - return newChunk; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java deleted file mode 100644 index 7753e87..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/Pot.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.pot; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.CCFertilizer; -import net.momirealms.customcrops.api.object.CCPot; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import org.jetbrains.annotations.NotNull; - -import java.io.Serial; -import java.io.Serializable; - -public class Pot implements Serializable, CCPot { - - @Serial - private static final long serialVersionUID = -6598493908660891824L; - - private Fertilizer fertilizer; - private int water; - private final String key; - - public Pot(String key, Fertilizer fertilizer, int water) { - this.key = key; - this.fertilizer = fertilizer; - this.water = water; - } - - public Fertilizer getFertilizer() { - return fertilizer; - } - - @Override - public void setFertilizer(CCFertilizer fertilizer) { - setFertilizer((Fertilizer) fertilizer); - } - - public void setFertilizer(Fertilizer fertilizer) { - this.fertilizer = fertilizer; - } - - public int getWater() { - return water; - } - - /* - whether to change block model - */ - public boolean addWater(int amount) { - if (water == 0) { - this.water = Math.min(getConfig().getMaxStorage(), amount); - return true; - } else { - this.water = Math.min(getConfig().getMaxStorage(), water + amount); - return false; - } - } - - public void setWater(int amount) { - this.water = amount; - } - - /* - whether to change block model - */ - public boolean reduceWater() { - if (water == 0) return false; - water--; - water = Math.max(0, water); - return water == 0; - } - - /* - whether to change block model - */ - public boolean reduceFertilizer() { - if (this.fertilizer != null && fertilizer.reduceTimes()) { - this.fertilizer = null; - return true; - } - return false; - } - - public boolean isWet() { - return water != 0; - } - - @NotNull - public String getKey() { - return key; - } - - public PotConfig getConfig() { - return CustomCrops.getInstance().getPotManager().getPotConfig(key); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/PotConfig.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/PotConfig.java deleted file mode 100644 index 3b13ba1..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/PotConfig.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.pot; - -import net.momirealms.customcrops.api.object.CCFertilizer; -import net.momirealms.customcrops.api.object.Pair; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerType; -import net.momirealms.customcrops.api.object.fill.PassiveFillMethod; -import net.momirealms.customcrops.api.object.hologram.FertilizerHologram; -import net.momirealms.customcrops.api.object.hologram.WaterAmountHologram; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; - -public class PotConfig { - - private final String key; - private final HashMap> fertilizerConvertMap; - private final int max_storage; - private final Pair pot; - private final boolean enableFertilized; - private final PassiveFillMethod[] passiveFillMethods; - private final FertilizerHologram fertilizerHologram; - private final WaterAmountHologram waterAmountHologram; - private final String potInfoItem; - - public PotConfig(String key, int max_storage, String dry_pot, String wet_pot, boolean enableFertilized, - @Nullable PassiveFillMethod[] passiveFillMethods, - @Nullable FertilizerHologram fertilizerHologram, - @Nullable WaterAmountHologram waterAmountHologram, - String potInfoItem) { - this.key = key; - this.max_storage = max_storage; - this.pot = Pair.of(dry_pot, wet_pot); - this.enableFertilized = enableFertilized; - this.fertilizerConvertMap = new HashMap<>(); - this.passiveFillMethods = passiveFillMethods; - this.fertilizerHologram = fertilizerHologram; - this.waterAmountHologram = waterAmountHologram; - this.potInfoItem = potInfoItem; - } - - public void registerFertilizedPot(FertilizerType fertilizerType, String dry_pot, String wet_pot) { - fertilizerConvertMap.put(fertilizerType, Pair.of(dry_pot, wet_pot)); - } - - public String getWetPot(@Nullable CCFertilizer CCFertilizer) { - if (!enableFertilized || !(CCFertilizer instanceof Fertilizer fertilizer)) return pot.right(); - FertilizerConfig fertilizerConfig = fertilizer.getConfig(); - if (fertilizerConfig == null) return pot.right(); - FertilizerType fertilizerType = fertilizerConfig.getFertilizerType(); - Pair pair = fertilizerConvertMap.get(fertilizerType); - if (pair == null) return pot.right(); - else return pair.right(); - } - - public String getDryPot(@Nullable CCFertilizer CCFertilizer) { - if (!enableFertilized || !(CCFertilizer instanceof Fertilizer fertilizer)) return pot.left(); - FertilizerConfig fertilizerConfig = fertilizer.getConfig(); - if (fertilizerConfig == null) return pot.left(); - FertilizerType fertilizerType = fertilizerConfig.getFertilizerType(); - Pair pair = fertilizerConvertMap.get(fertilizerType); - if (pair == null) return pot.left(); - else return pair.left(); - } - - public int getMaxStorage() { - return max_storage; - } - - @Nullable - public PassiveFillMethod[] getPassiveFillMethods() { - return passiveFillMethods; - } - - @Nullable - public FertilizerHologram getFertilizerHologram() { - return fertilizerHologram; - } - - @Nullable - public WaterAmountHologram getWaterAmountHologram() { - return waterAmountHologram; - } - - @Nullable - public String getPotInfoItem() { - return potInfoItem; - } - - @NotNull - public String getKey() { - return key; - } - - public boolean enableFertilizedLooks() { - return enableFertilized; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/PotManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/PotManager.java deleted file mode 100644 index ae38e61..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/pot/PotManager.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.pot; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerType; -import net.momirealms.customcrops.api.object.hologram.FertilizerHologram; -import net.momirealms.customcrops.api.object.hologram.HologramManager; -import net.momirealms.customcrops.api.object.hologram.TextDisplayMeta; -import net.momirealms.customcrops.api.object.hologram.WaterAmountHologram; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.HashMap; -import java.util.Locale; - -public class PotManager extends Function { - - private final CustomCrops plugin; - private final HashMap potConfigMap; - private final HashMap blockToPotKey; - public static boolean enableFarmLand; - public static boolean enableVanillaBlock; - - public PotManager(CustomCrops plugin) { - this.plugin = plugin; - this.potConfigMap = new HashMap<>(); - this.blockToPotKey = new HashMap<>(); - } - - @Override - public void load() { - loadConfig(); - } - - @Override - public void unload() { - this.potConfigMap.clear(); - this.blockToPotKey.clear(); - enableFarmLand = false; - enableVanillaBlock = false; - } - - private void loadConfig() { - File pot_folder = new File(plugin.getDataFolder(), "contents" + File.separator + "pots"); - if (!pot_folder.exists()) { - if (!pot_folder.mkdirs()) return; - ConfigUtils.getConfig("contents" + File.separator + "pots" + File.separator + "default.yml"); - } - File[] files = pot_folder.listFiles(); - if (files == null) return; - for (File file : files) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (String key : config.getKeys(false)) { - ConfigurationSection section = config.getConfigurationSection(key); - if (section == null) continue; - boolean enableFertilized = section.getBoolean("fertilized-pots.enable", false); - String base_dry = section.getString("base.dry"); - String base_wet = section.getString("base.wet"); - if (base_wet == null || base_dry == null) { - AdventureUtils.consoleMessage("[CustomCrops] base.dry/base.wet is not correctly set for pot: " + key); - continue; - } - - if (ConfigUtils.isVanillaItem(base_wet) || ConfigUtils.isVanillaItem(base_dry)) enableVanillaBlock = true; - - blockToPotKey.put(base_wet, key); - blockToPotKey.put(base_dry, key); - PotConfig potConfig = new PotConfig( - key, - section.getInt("max-water-storage"), - base_dry, - base_wet, - enableFertilized, - ConfigUtils.getPassiveFillMethods(section.getConfigurationSection("fill-method")), - section.getBoolean("hologram.fertilizer.enable", false) ? new FertilizerHologram( - section.getString("hologram.fertilizer.content", ""), - section.getDouble("hologram.fertilizer.vertical-offset"), - HologramManager.Mode.valueOf(section.getString("hologram.type", "ARMOR_STAND").toUpperCase(Locale.ENGLISH)), - section.getInt("hologram.duration"), - new TextDisplayMeta( - section.getBoolean("hologram.text-display-options.has-shadow", false), - section.getBoolean("hologram.text-display-options.is-see-through", false), - section.getBoolean("hologram.text-display-options.use-default-background-color", false), - ConfigUtils.rgbToDecimal(section.getString("hologram.text-display-options.background-color", "0,0,0,128")), - (byte) section.getInt("hologram.text-display-options.text-opacity") - ) - ) : null, - section.getBoolean("hologram.water.enable", false) ? new WaterAmountHologram( - section.getString("hologram.water.content", ""), - section.getDouble("hologram.water.vertical-offset"), - HologramManager.Mode.valueOf(section.getString("hologram.type", "ARMOR_STAND").toUpperCase(Locale.ENGLISH)), - section.getInt("hologram.duration"), - section.getString("hologram.water.water-bar.left"), - section.getString("hologram.water.water-bar.full"), - section.getString("hologram.water.water-bar.empty"), - section.getString("hologram.water.water-bar.right"), - new TextDisplayMeta( - section.getBoolean("hologram.text-display-options.has-shadow", false), - section.getBoolean("hologram.text-display-options.is-see-through", false), - section.getBoolean("hologram.text-display-options.use-default-background-color", false), - ConfigUtils.rgbToDecimal(section.getString("hologram.text-display-options.background-color", "0,0,0,128")), - (byte) section.getInt("hologram.text-display-options.text-opacity") - ) - ) : null, - section.getString("hologram.require-item") - ); - - if (enableFertilized) { - ConfigurationSection fertilizedSec = section.getConfigurationSection("fertilized-pots"); - if (fertilizedSec == null) continue; - for (String type : fertilizedSec.getKeys(false)) { - if (type.equals("enable")) continue; - String dry = fertilizedSec.getString(type + ".dry"); - String wet = fertilizedSec.getString(type + ".wet"); - blockToPotKey.put(dry, key); - blockToPotKey.put(wet, key); - switch (type) { - case "quality" -> potConfig.registerFertilizedPot(FertilizerType.QUALITY, dry, wet); - case "yield-increase" -> potConfig.registerFertilizedPot(FertilizerType.YIELD_INCREASE, dry, wet); - case "variation" -> potConfig.registerFertilizedPot(FertilizerType.VARIATION, dry, wet); - case "soil-retain" -> potConfig.registerFertilizedPot(FertilizerType.SOIL_RETAIN, dry, wet); - case "speed-grow" -> potConfig.registerFertilizedPot(FertilizerType.SPEED_GROW, dry, wet); - } - } - } - - if (base_dry.equals("FARMLAND") || base_wet.equals("FARMLAND")) { - enableFarmLand = true; - if (!ConfigManager.disableMoistureMechanic && (potConfig.getPassiveFillMethods() != null || potConfig.getWaterAmountHologram() != null)) { - AdventureUtils.consoleMessage("[CustomCrops] Since you are using vanilla farmland, vanilla moisture would"); - AdventureUtils.consoleMessage("[CustomCrops] conflict with CustomCrops' water system. It's advised to disable"); - AdventureUtils.consoleMessage("[CustomCrops] moisture mechanic in config.yml or delete fill-method and"); - AdventureUtils.consoleMessage("[CustomCrops] disable the water info hologram in pot configuration."); - } - } - - potConfigMap.put(key, potConfig); - } - } - AdventureUtils.consoleMessage("[CustomCrops] Loaded " + potConfigMap.size() + " pot(s)"); - } - - public boolean containsPotBlock(String id) { - return blockToPotKey.containsKey(id); - } - - @Nullable - public PotConfig getPotConfig(String key) { - return potConfigMap.get(key); - } - - @Nullable - public String getPotKeyByBlockID(String id) { - return blockToPotKey.get(id); - } - - @Nullable - public PotConfig getPotConfigByBlockID(String id) { - String key = blockToPotKey.get(id); - if (key == null) return null; - return potConfigMap.get(key); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/AbstractRequirement.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/AbstractRequirement.java deleted file mode 100644 index f297ede..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/AbstractRequirement.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -public abstract class AbstractRequirement { - - protected String[] msg; - protected Action[] actions; - - protected AbstractRequirement(@Nullable String[] msg, @Nullable Action[] actions) { - this.msg = msg; - this.actions = actions; - } - - public void notMetActions(CurrentState currentState) { - Player player = currentState.getPlayer(); - if (msg != null && player != null) { - for (String str : msg) { - AdventureUtils.playerMessage(player, str); - } - } - if (actions != null) { - for (Action action : actions) { - action.doOn(player, SimpleLocation.getByBukkitLocation(currentState.getLocation()), null); - } - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/BiomeImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/BiomeImpl.java deleted file mode 100644 index 1687048..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/BiomeImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.biomeapi.BiomeAPI; -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -import java.util.HashSet; - -public class BiomeImpl extends AbstractRequirement implements Requirement { - - private final HashSet biomes; - - public BiomeImpl(@Nullable String[] msg, @Nullable Action[] actions, HashSet biomes) { - super(msg, actions); - this.biomes = biomes; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - String currentBiome = BiomeAPI.getBiome(currentState.getLocation()); - if (biomes.contains(currentBiome)) { - return true; - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/BlackBiomeImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/BlackBiomeImpl.java deleted file mode 100644 index 408aa17..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/BlackBiomeImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.biomeapi.BiomeAPI; -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -import java.util.HashSet; - -public class BlackBiomeImpl extends AbstractRequirement implements Requirement { - - private final HashSet biomes; - - public BlackBiomeImpl(@Nullable String[] msg, @Nullable Action[] actions, HashSet biomes) { - super(msg, actions); - this.biomes = biomes; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - String currentBiome = BiomeAPI.getBiome(currentState.getLocation()); - if (!biomes.contains(currentBiome)) { - return true; - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/CustomPapi.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/CustomPapi.java deleted file mode 100644 index a97db58..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/CustomPapi.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.api.object.requirement.papi.*; -import org.bukkit.configuration.MemorySection; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class CustomPapi extends AbstractRequirement implements Requirement { - - private final List papiRequirement; - - public CustomPapi(String[] msg, @Nullable Action[] actions, Map expressions){ - super(msg, actions); - papiRequirement = getRequirements(expressions); - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - Player player = currentState.getPlayer(); - if (currentState.getPlayer() == null) return true; - for (PapiRequirement requirement : papiRequirement) { - if (!requirement.isMet(player)) { - notMetActions(currentState); - return false; - } - } - return true; - } - - private List getRequirements(Map map) { - List papiRequirements = new ArrayList<>(); - map.keySet().forEach(key -> { - if (key.startsWith("&&")) { - if (map.get(key) instanceof MemorySection map2) { - papiRequirements.add(new ExpressionAnd(getRequirements(map2.getValues(false)))); - } - } else if (key.startsWith("||")) { - if (map.get(key) instanceof MemorySection map2) { - papiRequirements.add(new ExpressionOr(getRequirements(map2.getValues(false)))); - } - } else { - if (map.get(key) instanceof MemorySection map2) { - String type = map2.getString("type"); - String papi = map2.getString("papi"); - String value = map2.getString("value"); - if (value == null || papi == null || type == null) return; - switch (type){ - case "==" -> papiRequirements.add(new PapiEquals(papi, value)); - case "!=" -> papiRequirements.add(new PapiNotEquals(papi, value)); - case ">=" -> papiRequirements.add(new PapiNoLess(papi, value)); - case "<=" -> papiRequirements.add(new PapiNoLarger(papi, value)); - case "<" -> papiRequirements.add(new PapiSmaller(papi, value)); - case ">" -> papiRequirements.add(new PapiGreater(papi, value)); - } - } - } - }); - return papiRequirements; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/DateImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/DateImpl.java deleted file mode 100644 index e4bd737..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/DateImpl.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -import java.util.Calendar; -import java.util.HashSet; - -public class DateImpl extends AbstractRequirement implements Requirement { - - private final HashSet dates; - - public DateImpl(String[] msg, @Nullable Action[] actions, HashSet dates) { - super(msg, actions); - this.dates = dates; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - Calendar calendar = Calendar.getInstance(); - String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE); - if (dates.contains(current)) { - return true; - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/EntityAmountInChunkImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/EntityAmountInChunkImpl.java deleted file mode 100644 index 7b389ab..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/EntityAmountInChunkImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -public class EntityAmountInChunkImpl extends AbstractRequirement implements Requirement { - - private final int amount; - - public EntityAmountInChunkImpl(@Nullable String[] msg, @Nullable Action[] actions, int amount) { - super(msg, actions); - this.amount = amount; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - if (currentState.getLocation().getChunk().getEntities().length <= amount) { - return true; - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/JobLevelImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/JobLevelImpl.java deleted file mode 100644 index d6c5256..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/JobLevelImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.integration.JobInterface; -import org.jetbrains.annotations.Nullable; - -public class JobLevelImpl extends AbstractRequirement implements Requirement { - - private final int level; - private final String jobName; - - public JobLevelImpl(@Nullable String[] msg, @Nullable Action[] actions, int level, String jobName) { - super(msg, actions); - this.level = level; - this.jobName = jobName; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - JobInterface jobInterface = CustomCrops.getInstance().getIntegrationManager().getJobInterface(); - if (jobInterface == null || currentState.getPlayer() == null) return true; - if (jobInterface.getLevel(currentState.getPlayer(), jobName) >= level) { - return true; - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/LightLevelImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/LightLevelImpl.java deleted file mode 100644 index a4379f2..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/LightLevelImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -public class LightLevelImpl extends AbstractRequirement implements Requirement { - - private final int level; - - public LightLevelImpl(@Nullable String[] msg, @Nullable Action[] actions, int level) { - super(msg, actions); - this.level = level; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - if (currentState.getLocation().getBlock().getLightLevel() >= level) { - return true; - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/NaturalLightLevelImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/NaturalLightLevelImpl.java deleted file mode 100644 index b307c00..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/NaturalLightLevelImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -public class NaturalLightLevelImpl extends AbstractRequirement implements Requirement { - - private final int level; - - public NaturalLightLevelImpl(@Nullable String[] msg, @Nullable Action[] actions, int level) { - super(msg, actions); - this.level = level; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - if (currentState.getLocation().getBlock().getLightFromSky() >= level) { - return true; - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/PermissionImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/PermissionImpl.java deleted file mode 100644 index f59b87c..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/PermissionImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -public class PermissionImpl extends AbstractRequirement implements Requirement { - - private final String permission; - - public PermissionImpl(@Nullable String[] msg, @Nullable Action[] actions, String permission) { - super(msg, actions); - this.permission = permission; - } - - public String getPermission() { - return this.permission; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - if (currentState.getPlayer() == null || currentState.getPlayer().hasPermission(permission)) { - return true; - } - notMetActions(currentState); - return false; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/SeasonImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/SeasonImpl.java deleted file mode 100644 index 58084c2..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/SeasonImpl.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.api.object.world.WorldDataManager; -import net.momirealms.customcrops.integration.SeasonInterface; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class SeasonImpl extends AbstractRequirement implements Requirement { - - private final List seasons; - - public SeasonImpl(@Nullable String[] msg, @Nullable Action[] actions, List seasons) { - super(msg, actions); - this.seasons = seasons; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - SeasonInterface seasonInterface = CustomCrops.getInstance().getIntegrationManager().getSeasonInterface(); - CCSeason currentSeason = seasonInterface.getSeason(currentState.getLocation().getWorld().getName()); - if (seasons.contains(currentSeason)) { - return true; - } - - SimpleLocation simpleLocation = SimpleLocation.getByBukkitLocation(currentState.getLocation()); - WorldDataManager worldDataManager = CustomCrops.getInstance().getWorldDataManager(); - if (ConfigManager.enableGreenhouse) { - for (int i = 0; i < ConfigManager.greenhouseRange; i++) { - if (worldDataManager.isGreenhouse(simpleLocation.add(0, i, 0))) { - return true; - } - } - } - notMetActions(currentState); - return false; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/SkillLevelImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/SkillLevelImpl.java deleted file mode 100644 index ffec5b0..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/SkillLevelImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.integration.SkillInterface; -import org.jetbrains.annotations.Nullable; - -public class SkillLevelImpl extends AbstractRequirement implements Requirement { - - private final int level; - - public SkillLevelImpl(@Nullable String[] msg, @Nullable Action[] actions, int level) { - super(msg, actions); - this.level = level; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - SkillInterface skillInterface = CustomCrops.getInstance().getIntegrationManager().getSkillInterface(); - if (skillInterface == null || currentState.getPlayer() == null) return true; - if (skillInterface.getLevel(currentState.getPlayer()) >= level) { - return true; - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/TimeImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/TimeImpl.java deleted file mode 100644 index fa1403a..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/TimeImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class TimeImpl extends AbstractRequirement implements Requirement { - - private final List times; - - public TimeImpl(@Nullable String[] msg, @Nullable Action[] actions, List times) { - super(msg, actions); - this.times = times; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - long time = currentState.getLocation().getWorld().getTime(); - for (String range : times) { - String[] timeMinMax = range.split("~"); - if (time > Long.parseLong(timeMinMax[0]) && time < Long.parseLong(timeMinMax[1])) { - return true; - } - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/WeatherImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/WeatherImpl.java deleted file mode 100644 index 22ab8e9..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/WeatherImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.bukkit.World; -import org.jetbrains.annotations.Nullable; - -public class WeatherImpl extends AbstractRequirement implements Requirement { - - private final String[] weathers; - - public WeatherImpl(@Nullable String[] msg, @Nullable Action[] actions, String[] weathers) { - super(msg, actions); - this.weathers = weathers; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - World world = currentState.getLocation().getWorld(); - String currentWeather; - if (world.isThundering()) currentWeather = "thunder"; - else if (world.isClearWeather()) currentWeather = "clear"; - else currentWeather = "rain"; - for (String weather : weathers) { - if (weather.equals(currentWeather)) { - return true; - } - } - notMetActions(currentState); - return false; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/WorldImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/WorldImpl.java deleted file mode 100644 index 7b55215..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/WorldImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.bukkit.World; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class WorldImpl extends AbstractRequirement implements Requirement { - - private final List worlds; - - public WorldImpl(@Nullable String[] msg, @Nullable Action[] actions, List worlds) { - super(msg, actions); - this.worlds = worlds; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - World world = currentState.getLocation().getWorld(); - if (worlds.contains(world.getName())) { - return true; - } - notMetActions(currentState); - return false; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/YPosImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/YPosImpl.java deleted file mode 100644 index 9918fcd..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/YPosImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement; - -import net.momirealms.customcrops.api.object.action.Action; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class YPosImpl extends AbstractRequirement implements Requirement { - - private final List yPos; - - public YPosImpl(@Nullable String[] msg, @Nullable Action[] actions, List yPos) { - super(msg, actions); - this.yPos = yPos; - } - - @Override - public boolean isConditionMet(CurrentState currentState) { - int y = (int) currentState.getLocation().getY(); - for (String range : yPos) { - String[] yMinMax = range.split("~"); - if (y > Integer.parseInt(yMinMax[0]) && y < Integer.parseInt(yMinMax[1])) { - return true; - } - } - notMetActions(currentState); - return false; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiGreater.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiGreater.java deleted file mode 100644 index f8919db..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiGreater.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.entity.Player; - -public record PapiGreater(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(Player player) { - double value = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, papi)); - return value > Double.parseDouble(PlaceholderAPI.setPlaceholders(player, requirement)); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNoLess.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNoLess.java deleted file mode 100644 index cefc664..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNoLess.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.entity.Player; - -public record PapiNoLess(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(Player player) { - double value = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, papi)); - return value >= Double.parseDouble(PlaceholderAPI.setPlaceholders(player, requirement)); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNotEquals.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNotEquals.java deleted file mode 100644 index ecebf26..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNotEquals.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.entity.Player; - -import java.util.Objects; - -public record PapiNotEquals(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(Player player) { - String value = PlaceholderAPI.setPlaceholders(player, papi); - return !Objects.equals(value, PlaceholderAPI.setPlaceholders(player, requirement)); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiSmaller.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiSmaller.java deleted file mode 100644 index 6040540..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiSmaller.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.requirement.papi; - -import me.clip.placeholderapi.PlaceholderAPI; -import org.bukkit.entity.Player; - -public record PapiSmaller(String papi, String requirement) implements PapiRequirement{ - - @Override - public boolean isMet(Player player) { - double value = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, papi)); - return value < Double.parseDouble(PlaceholderAPI.setPlaceholders(player, requirement)); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/FoliaSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/FoliaSchedulerImpl.java deleted file mode 100644 index f004f45..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/FoliaSchedulerImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -//package net.momirealms.customcrops.api.object.scheduler; -// -//import net.momirealms.customcrops.CustomCrops; -//import org.bukkit.Bukkit; -//import org.bukkit.Location; -//import org.jetbrains.annotations.NotNull; -// -//import java.util.concurrent.Callable; -//import java.util.concurrent.Future; -// -//public class FoliaSchedulerImpl implements SchedulerPlatform { -// -// private final CustomCrops plugin; -// -// public FoliaSchedulerImpl(CustomCrops plugin) { -// this.plugin = plugin; -// } -// -// @Override -// public Future callSyncMethod(@NotNull Callable task) { -// return null; -// } -// -// @Override -// public void runTask(Runnable runnable) { -// Bukkit.getGlobalRegionScheduler().execute(plugin, runnable); -// } -// -// @Override -// public void runTask(Runnable runnable, Location location) { -// Bukkit.getRegionScheduler().execute(plugin, location, runnable); -// } -//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/Scheduler.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/Scheduler.java deleted file mode 100644 index e8fc49f..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/Scheduler.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.scheduler; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.*; - -public class Scheduler extends Function { - - private final ScheduledThreadPoolExecutor schedule; - private final SchedulerPlatform schedulerPlatform; - - public Scheduler(CustomCrops plugin) { - this.schedulerPlatform = new BukkitSchedulerImpl(plugin); - this.schedule = new ScheduledThreadPoolExecutor(1); - this.schedule.setMaximumPoolSize(2); - this.schedule.setKeepAliveTime(ConfigManager.keepAliveTime, TimeUnit.SECONDS); - this.schedule.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); - } - - @Override - public void disable() { - this.schedule.shutdown(); - } - - public ScheduledFuture runTaskAsyncLater(Runnable runnable, long delay) { - return this.schedule.schedule(runnable, delay, TimeUnit.MILLISECONDS); - } - - public void runTaskAsync(Runnable runnable) { - this.schedule.execute(runnable); - } - - public void runTask(Runnable runnable) { - this.schedulerPlatform.runTask(runnable); - } - - public Future callSyncMethod(@NotNull Callable task) { - return this.schedulerPlatform.callSyncMethod(task); - } - - public ScheduledFuture runTaskTimerAsync(Runnable runnable, long delay, long interval) { - return this.schedule.scheduleAtFixedRate(runnable, delay, interval, TimeUnit.MILLISECONDS); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/season/SeasonData.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/season/SeasonData.java deleted file mode 100644 index f2c1d18..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/season/SeasonData.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.season; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.event.SeasonChangeEvent; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import org.bukkit.Bukkit; - -import java.util.Objects; - -public class SeasonData { - - private CCSeason ccSeason; - private int date; - private final String world; - - public SeasonData(String world, CCSeason ccSeason, int date) { - this.world = world; - this.ccSeason = ccSeason; - this.date = date; - } - - public SeasonData(String world) { - this.world = world; - this.ccSeason = CCSeason.SPRING; - this.date = 1; - } - - public CCSeason getSeason() { - return ccSeason; - } - - public int getDate() { - return date; - } - - public void addDate() { - this.date++; - if (date > ConfigManager.seasonInterval) { - this.date = 1; - this.ccSeason = getNextSeason(ccSeason); - CustomCrops.getInstance().getScheduler().runTask(this::callEvent); - } - } - - public CCSeason getNextSeason(CCSeason ccSeason) { - return switch (ccSeason) { - case AUTUMN -> CCSeason.WINTER; - case WINTER -> CCSeason.SPRING; - case SPRING -> CCSeason.SUMMER; - case SUMMER -> CCSeason.AUTUMN; - default -> CCSeason.UNKNOWN; - }; - } - - public void changeSeason(CCSeason ccSeason) { - if (ccSeason != this.ccSeason) { - this.ccSeason = ccSeason; - callEvent(); - } - } - - public String getWorld() { - return world; - } - - public void setDate(int date) { - this.date = date; - } - - private void callEvent() { - SeasonChangeEvent seasonChangeEvent = new SeasonChangeEvent(Objects.requireNonNull(Bukkit.getWorld(world)), ccSeason); - Bukkit.getPluginManager().callEvent(seasonChangeEvent); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/season/SeasonManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/season/SeasonManager.java deleted file mode 100644 index 74a9bb4..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/season/SeasonManager.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.season; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.ConcurrentHashMap; - -public class SeasonManager extends Function { - - private final CustomCrops plugin; - private final ConcurrentHashMap seasonMap; - - public SeasonManager(CustomCrops plugin) { - this.plugin = plugin; - this.seasonMap = new ConcurrentHashMap<>(4); - } - - @Override - public void disable() { - this.seasonMap.clear(); - } - - @Nullable - public SeasonData getSeasonData(String world) { - return seasonMap.get(world); - } - - public void loadSeasonData(SeasonData seasonData) { - seasonMap.put(seasonData.getWorld(), seasonData); - } - - @Nullable - public SeasonData unloadSeasonData(String world) { - return seasonMap.remove(world); - } - - public CCSeason getSeason(String world) { - SeasonData seasonData = seasonMap.get(ConfigManager.syncSeason ? ConfigManager.referenceWorld : world); - if (seasonData == null) { - return CCSeason.UNKNOWN; - } - return seasonData.getSeason(); - } - - public void addDate(String world) { - SeasonData seasonData = seasonMap.get(world); - if (seasonData != null) seasonData.addDate(); - } - - public int getDate(String world) { - SeasonData seasonData = seasonMap.get(ConfigManager.syncSeason ? ConfigManager.referenceWorld : world); - if (seasonData == null) return -1; - return seasonData.getDate(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/Sprinkler.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/Sprinkler.java deleted file mode 100644 index 9015a79..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/Sprinkler.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.sprinkler; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.CCSprinkler; -import org.jetbrains.annotations.Nullable; - -import java.io.Serial; -import java.io.Serializable; - -public class Sprinkler implements Serializable, CCSprinkler { - - @Serial - private static final long serialVersionUID = -1994328062935821245L; - - private int water; - private final String key; - - public Sprinkler(String key, int water) { - this.water = water; - this.key = key; - } - - public int getWater() { - return water; - } - - public void setWater(int water) { - this.water = water; - } - - public String getKey() { - return key; - } - - @Nullable - public SprinklerConfig getConfig() { - return CustomCrops.getInstance().getSprinklerManager().getConfigByKey(key); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerAnimation.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerAnimation.java deleted file mode 100644 index 7c1ce4b..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerAnimation.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.momirealms.customcrops.api.object.sprinkler; - -import net.momirealms.customcrops.api.object.ItemMode; - -public record SprinklerAnimation(int duration, String id, double offset, ItemMode itemMode) { - -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerConfig.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerConfig.java deleted file mode 100644 index fc0cba7..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerConfig.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.sprinkler; - -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.fill.PassiveFillMethod; -import net.momirealms.customcrops.api.object.hologram.WaterAmountHologram; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SprinklerConfig { - - private final String key; - private final int storage; - private final int range; - private final Sound sound; - private final ItemMode itemMode; - private final String threeD; - private final String twoD; - private final String[] potWhitelist; - private final PassiveFillMethod[] passiveFillMethods; - private final WaterAmountHologram waterAmountHologram; - private final SprinklerAnimation sprinklerAnimation; - private final Requirement[] requirements; - private final int water; - private boolean infinite; - - public SprinklerConfig( - String key, - int storage, - int range, - int water, - @Nullable String[] potWhitelist, - @Nullable Sound sound, - @NotNull ItemMode itemMode, - @NotNull String threeD, - @Nullable String twoD, - @NotNull PassiveFillMethod[] passiveFillMethods, - @Nullable WaterAmountHologram waterAmountHologram, - SprinklerAnimation sprinklerAnimation, - @Nullable Requirement[] requirements, - boolean infinite - ) { - this.key = key; - this.storage = storage; - this.range = range; - this.water = water; - this.potWhitelist = potWhitelist; - this.sound = sound; - this.itemMode = itemMode; - this.threeD = threeD; - this.twoD = twoD; - this.passiveFillMethods = passiveFillMethods; - this.sprinklerAnimation = sprinklerAnimation; - this.waterAmountHologram = waterAmountHologram; - this.requirements = requirements; - this.infinite = infinite; - } - - public String getKey() { - return key; - } - - public int getStorage() { - return storage; - } - - public int getRange() { - return range; - } - - @Nullable - public Sound getSound() { - return sound; - } - - @NotNull - public ItemMode getItemMode() { - return itemMode; - } - - @NotNull - public String getThreeD() { - return threeD; - } - - @Nullable - public String getTwoD() { - return twoD; - } - - @NotNull - public PassiveFillMethod[] getPassiveFillMethods() { - return passiveFillMethods; - } - - @Nullable - public WaterAmountHologram getSprinklerHologram() { - return waterAmountHologram; - } - - @Nullable - public SprinklerAnimation getSprinklerAnimation() { - return sprinklerAnimation; - } - - @Nullable - public String[] getPotWhitelist() { - return potWhitelist; - } - - public int getWaterFillAbility() { - return water; - } - - public boolean isInfinite() { - return infinite; - } - - @Nullable - public Requirement[] getRequirements() { - return requirements; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerManager.java deleted file mode 100644 index 00c9f5d..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/sprinkler/SprinklerManager.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.sprinkler; - -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.fill.PassiveFillMethod; -import net.momirealms.customcrops.api.object.hologram.HologramManager; -import net.momirealms.customcrops.api.object.hologram.TextDisplayMeta; -import net.momirealms.customcrops.api.object.hologram.WaterAmountHologram; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Item; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.ItemSpawnEvent; -import org.bukkit.inventory.ItemStack; -import org.intellij.lang.annotations.Subst; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.HashMap; -import java.util.Locale; - -public class SprinklerManager extends Function implements Listener { - - private final CustomCrops plugin; - private final HashMap sprinklerConfigMap; - private final HashMap itemToKey; - - public SprinklerManager(CustomCrops plugin) { - this.plugin = plugin; - this.sprinklerConfigMap = new HashMap<>(); - this.itemToKey = new HashMap<>(); - } - - @Override - public void load() { - this.loadConfig(); - Bukkit.getPluginManager().registerEvents(this, plugin); - } - - @Override - public void unload() { - this.sprinklerConfigMap.clear(); - this.itemToKey.clear(); - HandlerList.unregisterAll(this); - } - - private void loadConfig() { - File sprinkler_folder = new File(plugin.getDataFolder(), "contents" + File.separator + "sprinklers"); - if (!sprinkler_folder.exists()) { - if (!sprinkler_folder.mkdirs()) return; - ConfigUtils.getConfig("contents" + File.separator + "sprinklers" + File.separator + "default.yml"); - } - File[] files = sprinkler_folder.listFiles(); - if (files == null) return; - for (File file : files) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (String key : config.getKeys(false)) { - ConfigurationSection sprinklerSec = config.getConfigurationSection(key); - if (sprinklerSec == null) continue; - String twoD = sprinklerSec.getString("2D-item"); - String threeD = sprinklerSec.getString("3D-item"); - if (threeD == null) { - AdventureUtils.consoleMessage("[CustomCrops] 3D-item is not set for sprinkler: " + key); - continue; - } - PassiveFillMethod[] methods = ConfigUtils.getPassiveFillMethods(sprinklerSec.getConfigurationSection("fill-method")); - if (methods == null) { - AdventureUtils.consoleMessage("[CustomCrops] fill method is not set for sprinkler: " + key); - continue; - } - @Subst("namespace:key") String soundKey = sprinklerSec.getString("place-sound", "minecraft:block.bone_block.place"); - Sound sound = sprinklerSec.contains("place-sound") ? Sound.sound(Key.key(soundKey), Sound.Source.PLAYER, 1, 1) : null; - ItemMode itemMode = ItemMode.valueOf(sprinklerSec.getString("type","ITEM_FRAME").toUpperCase(Locale.ENGLISH)); - SprinklerConfig sprinklerConfig = new SprinklerConfig( - key, - sprinklerSec.getInt("storage", 3), - sprinklerSec.getInt("range", 1), - sprinklerSec.getInt("water",1), - sprinklerSec.contains("pot-whitelist") ? sprinklerSec.getStringList("pot-whitelist").toArray(new String[0]) : null, - sound, - itemMode, - threeD, - twoD, - methods, - sprinklerSec.getBoolean("hologram.enable") ? new WaterAmountHologram( - sprinklerSec.getString("hologram.content",""), - sprinklerSec.getDouble("hologram.vertical-offset"), - HologramManager.Mode.valueOf(sprinklerSec.getString("hologram.type", "ARMOR_STAND").toUpperCase(Locale.ENGLISH)), - sprinklerSec.getInt("hologram.duration"), - sprinklerSec.getString("hologram.water-bar.left"), - sprinklerSec.getString("hologram.water-bar.full"), - sprinklerSec.getString("hologram.water-bar.empty"), - sprinklerSec.getString("hologram.water-bar.right"), - new TextDisplayMeta( - sprinklerSec.getBoolean("hologram.text-display-options.has-shadow", false), - sprinklerSec.getBoolean("hologram.text-display-options.is-see-through", false), - sprinklerSec.getBoolean("hologram.text-display-options.use-default-background-color", false), - ConfigUtils.rgbToDecimal(sprinklerSec.getString("hologram.text-display-options.background-color", "0,0,0,128")), - (byte) sprinklerSec.getInt("hologram.text-display-options.text-opacity") - ) - ) : null, - sprinklerSec.getBoolean("animation.enable") ? new SprinklerAnimation( - sprinklerSec.getInt("animation.duration"), - sprinklerSec.getString("animation.item"), - sprinklerSec.getDouble("animation.vertical-offset"), - ItemMode.valueOf(sprinklerSec.getString("animation.type", "ARMOR_STAND").toUpperCase(Locale.ENGLISH)) - ) : null, - ConfigUtils.getRequirementsWithMsg(sprinklerSec.getConfigurationSection("requirements")), - sprinklerSec.getBoolean("infinite", false)); - this.itemToKey.put(threeD, key); - if (twoD != null) this.itemToKey.put(twoD, key); - this.sprinklerConfigMap.put(key, sprinklerConfig); - } - } - AdventureUtils.consoleMessage("[CustomCrops] Loaded " + sprinklerConfigMap.size() + " sprinkler(s)"); - } - - @Nullable - public SprinklerConfig getConfigByItemID(String id) { - String key = itemToKey.get(id); - if (key == null) return null; - return sprinklerConfigMap.get(key); - } - - @Nullable - public String getConfigKeyByItemID(String id) { - return itemToKey.get(id); - } - - @Nullable - public SprinklerConfig getConfigByKey(String key) { - return sprinklerConfigMap.get(key); - } - - @EventHandler - public void onItemSpawn(ItemSpawnEvent event) { - if (event.isCancelled()) return; - Item item = event.getEntity(); - ItemStack origin = item.getItemStack(); - String id = plugin.getPlatformInterface().getItemStackID(origin); - String key = itemToKey.get(id); - if (key == null) return; - String twoD = sprinklerConfigMap.get(key).getTwoD(); - if (twoD == null || id.equals(twoD)) return; - ItemStack itemStack = plugin.getPlatformInterface().getItemStack(twoD); - if (itemStack == null) { - AdventureUtils.consoleMessage("[CustomCrops] 2D sprinkler " + twoD + " doesn't exist"); - return; - } - itemStack.setAmount(origin.getAmount()); - item.setItemStack(itemStack); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/wateringcan/WateringCanConfig.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/wateringcan/WateringCanConfig.java deleted file mode 100644 index ee55ece..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/wateringcan/WateringCanConfig.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.wateringcan; - -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customcrops.api.object.fill.PositiveFillMethod; -import net.momirealms.customcrops.api.object.requirement.CurrentState; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.bukkit.Location; -import org.bukkit.Particle; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.List; -import java.util.Optional; - -public class WateringCanConfig { - - private final int width; - private final int length; - private final int storage; - private final String[] potWhitelist; - private final String[] sprinkler_whitelist; - private final boolean hasDynamicLore; - private final boolean hasActionBar; - private final Sound sound; - private final Particle particle; - private final List loreTemplate; - private final String actionBarMsg; - private final String bar_left; - private final String bar_full; - private final String bar_empty; - private final String bar_right; - private final PositiveFillMethod[] positiveFillMethods; - private final HashMap appearanceMap; - private final Requirement[] requirements; - - public WateringCanConfig( - int width, - int length, - int storage, - boolean hasDynamicLore, - boolean hasActionBar, - @Nullable List loreTemplate, - @Nullable String actionBarMsg, - @Nullable String bar_left, - @Nullable String bar_full, - @Nullable String bar_empty, - @Nullable String bar_right, - String[] potWhitelist, - String[] sprinkler_whitelist, - @Nullable Sound sound, - @Nullable Particle particle, - @NotNull PositiveFillMethod[] positiveFillMethods, - @NotNull HashMap appearanceMap, - @Nullable Requirement[] requirements - ) { - this.width = width; - this.length = length; - this.storage = storage; - this.hasDynamicLore = hasDynamicLore; - this.hasActionBar = hasActionBar; - this.loreTemplate = loreTemplate; - this.actionBarMsg = actionBarMsg; - this.bar_left = bar_left; - this.bar_full = bar_full; - this.bar_empty = bar_empty; - this.bar_right = bar_right; - this.potWhitelist = potWhitelist; - this.sprinkler_whitelist = sprinkler_whitelist; - this.sound = sound; - this.particle = particle; - this.positiveFillMethods = positiveFillMethods; - this.appearanceMap = appearanceMap; - this.requirements = requirements; - } - - public int getWidth() { - return width; - } - - public int getLength() { - return length; - } - - public int getStorage() { - return storage; - } - - public String getWaterBar(int current) { - return bar_left + - String.valueOf(bar_full).repeat(current) + - String.valueOf(bar_empty).repeat(Math.max(storage - current, 0)) + - bar_right; - } - - public boolean hasDynamicLore() { - return hasDynamicLore; - } - - public boolean hasActionBar() { - return hasActionBar; - } - - public String getActionBarMsg(int current) { - assert actionBarMsg != null; - return actionBarMsg - .replace("{current}", String.valueOf(current)) - .replace("{storage}", String.valueOf(storage)) - .replace("{water_bar}", getWaterBar(current)); - } - - public List getLore(int current) { - assert loreTemplate != null; - return loreTemplate.stream().map(line -> - GsonComponentSerializer.gson().serialize( - MiniMessage.miniMessage().deserialize(line - .replace("{current}", String.valueOf(current)) - .replace("{storage}", String.valueOf(storage)) - .replace("{water_bar}", getWaterBar(current))))).toList(); - } - - public String[] getPotWhitelist() { - return potWhitelist; - } - - public String[] getSprinklerWhitelist() { - return sprinkler_whitelist; - } - - @Nullable - public Sound getSound() { - return sound; - } - - @Nullable - public Particle getParticle() { - return particle; - } - - @NotNull - public PositiveFillMethod[] getPositiveFillMethods() { - return positiveFillMethods; - } - - public int getModelDataByWater(int water) { - return Optional.ofNullable(appearanceMap.get(water)).orElse(0); - } - - public boolean canUse(Player player, Location location) { - if (requirements == null) return true; - CurrentState currentState = new CurrentState(location, player); - for (Requirement requirement : requirements) { - if (!requirement.isConditionMet(currentState)) { - return false; - } - } - return true; - } - - public boolean isWhiteListedPot(String potID) { - if (potWhitelist != null) { - inner: { - for (String pot : potWhitelist) - if (pot.equals(potID)) - break inner; - return false; - } - } - return true; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/wateringcan/WateringCanManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/wateringcan/WateringCanManager.java deleted file mode 100644 index 2d47ba3..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/wateringcan/WateringCanManager.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.wateringcan; - -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTItem; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.fill.PositiveFillMethod; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Material; -import org.bukkit.Particle; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.inventory.ItemStack; -import org.intellij.lang.annotations.Subst; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -public class WateringCanManager extends Function { - - private final CustomCrops plugin; - private final HashMap wateringCanConfigMap; - - public WateringCanManager(CustomCrops plugin) { - this.plugin = plugin; - this.wateringCanConfigMap = new HashMap<>(); - } - - @Override - public void load() { - loadConfig(); - } - - @Override - public void unload() { - this.wateringCanConfigMap.clear(); - } - - @Nullable - public WateringCanConfig getConfigByItemID(String id) { - return wateringCanConfigMap.get(id); - } - - private void loadConfig() { - File can_folder = new File(plugin.getDataFolder(), "contents" + File.separator + "watering-cans"); - if (!can_folder.exists()) { - if (!can_folder.mkdirs()) return; - ConfigUtils.getConfig("contents" + File.separator + "watering-cans" + File.separator + "default.yml"); - } - File[] files = can_folder.listFiles(); - if (files == null) return; - for (File file : files) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - for (String key : config.getKeys(false)) { - ConfigurationSection canSec = config.getConfigurationSection(key); - if (canSec == null) continue; - PositiveFillMethod[] methods = ConfigUtils.getPositiveFillMethods(canSec.getConfigurationSection("fill-method")); - if (methods == null) { - AdventureUtils.consoleMessage("[CustomCrops] You need to at least one fill-method for: " + key); - continue; - } - ConfigurationSection appearSec = canSec.getConfigurationSection("appearance"); - HashMap appearanceMap = new HashMap<>(); - if (appearSec != null) { - for (Map.Entry entry : appearSec.getValues(false).entrySet()) { - appearanceMap.put(Integer.parseInt(entry.getKey()), (Integer) entry.getValue()); - } - } - @Subst("namespace:key") String soundKey = canSec.getString("sound", "minecraft:block.water.ambient"); - Sound sound = canSec.contains("sound") ? Sound.sound(Key.key(soundKey), Sound.Source.PLAYER, 1, 1) : null; - WateringCanConfig wateringCanConfig = new WateringCanConfig( - canSec.getInt("effective-range.width"), - canSec.getInt("effective-range.length"), - canSec.getInt("capacity"), - canSec.getBoolean("dynamic-lore.enable", false), - canSec.getBoolean("actionbar.enable", false), - canSec.getStringList("dynamic-lore.lore"), - canSec.getString("actionbar.content"), - canSec.getString("water-bar.left"), - canSec.getString("water-bar.full"), - canSec.getString("water-bar.empty"), - canSec.getString("water-bar.right"), - canSec.contains("pot-whitelist") ? canSec.getStringList("pot-whitelist").toArray(new String[0]) : null, - canSec.contains("sprinkler-whitelist") ? canSec.getStringList("sprinkler-whitelist").toArray(new String[0]) : null, - sound, - canSec.contains("particle") ? Particle.valueOf(canSec.getString("particle", "WATER_SPLASH").toUpperCase(Locale.ENGLISH)) : null, - methods, - appearanceMap, - ConfigUtils.getRequirementsWithMsg(canSec.getConfigurationSection("requirements")) - ); - wateringCanConfigMap.put(canSec.getString("item"), wateringCanConfig); - } - } - AdventureUtils.consoleMessage("[CustomCrops] Loaded " + wateringCanConfigMap.size() + " watering-can(s)"); - } - - public int getCurrentWater(ItemStack itemStack) { - if (itemStack.getType() == Material.AIR) return 0; - NBTItem nbtItem = new NBTItem(itemStack); - return nbtItem.getInteger("WaterAmount"); - } - - public void setWater(ItemStack itemStack, int water, WateringCanConfig config) { - if (itemStack.getType() == Material.AIR) return; - NBTItem nbtItem = new NBTItem(itemStack); - nbtItem.setInteger("WaterAmount", water); - if (config.hasDynamicLore()) { - NBTCompound display = nbtItem.getCompound("display"); - List lore = display.getStringList("Lore"); - lore.clear(); - lore.addAll(config.getLore(water)); - } - int cmd = config.getModelDataByWater(water); - if (cmd != 0) { - nbtItem.setInteger("CustomModelData", cmd); - } - itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java deleted file mode 100644 index 4a81d98..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/CCChunk.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.world; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.OfflineReplaceTask; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.sprinkler.Sprinkler; -import net.momirealms.customcrops.customplugin.PlatformInterface; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.data.type.Farmland; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadLocalRandom; - -public class CCChunk implements Serializable { - - @Serial - private static final long serialVersionUID = 5300805317167684402L; - - private final ConcurrentHashMap growingCropMap; - private final ConcurrentHashMap potMap; - private final ConcurrentHashMap sprinklerMap; - private ConcurrentHashMap replaceTaskMap; - private final Set greenhouseSet; - private final Set scarecrowSet; - - public CCChunk() { - this.growingCropMap = new ConcurrentHashMap<>(64); - this.potMap = new ConcurrentHashMap<>(64); - this.sprinklerMap = new ConcurrentHashMap<>(16); - this.greenhouseSet = Collections.synchronizedSet(new HashSet<>(64)); - this.scarecrowSet = Collections.synchronizedSet(new HashSet<>(4)); - this.replaceTaskMap = new ConcurrentHashMap<>(64); - } - - @Serial - private void writeObject(ObjectOutputStream oos) throws IOException { - oos.defaultWriteObject(); - } - - @Serial - private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { - ois.defaultReadObject(); - if (replaceTaskMap == null) { - replaceTaskMap = new ConcurrentHashMap<>(64); - } - } - - public void removeCropData(SimpleLocation simpleLocation) { - growingCropMap.remove(simpleLocation); - } - - public void addCropData(SimpleLocation simpleLocation, GrowingCrop growingCrop) { - growingCropMap.put(simpleLocation, growingCrop); - } - - @Nullable - public GrowingCrop getCropData(SimpleLocation simpleLocation) { - return growingCropMap.get(simpleLocation); - } - - public int getCropAmount() { - return growingCropMap.size(); - } - - @Nullable - public Pot getPotData(SimpleLocation simpleLocation) { - return potMap.get(simpleLocation); - } - - public void addPotData(SimpleLocation simpleLocation, Pot pot) { - potMap.put(simpleLocation, pot); - } - - public void removePotData(SimpleLocation simpleLocation) { - potMap.remove(simpleLocation); - } - - public void addGreenhouse(SimpleLocation simpleLocation) { - greenhouseSet.add(simpleLocation); - } - - public void removeGreenhouse(SimpleLocation simpleLocation) { - greenhouseSet.remove(simpleLocation); - } - - public boolean isGreenhouse(SimpleLocation simpleLocation) { - return greenhouseSet.contains(simpleLocation); - } - - public void addScarecrow(SimpleLocation simpleLocation) { - scarecrowSet.add(simpleLocation); - } - - public void removeScarecrow(SimpleLocation simpleLocation) { - scarecrowSet.remove(simpleLocation); - } - - public boolean hasScarecrow() { - return scarecrowSet.size() != 0; - } - - public boolean isUseless() { - return growingCropMap.size() == 0 && potMap.size() == 0 && greenhouseSet.size() == 0 && sprinklerMap.size() == 0 && scarecrowSet.size() == 0 && replaceTaskMap.size() == 0; - } - - @Nullable - public Sprinkler getSprinklerData(SimpleLocation simpleLocation) { - return sprinklerMap.get(simpleLocation); - } - - public void removeSprinklerData(SimpleLocation simpleLocation) { - sprinklerMap.remove(simpleLocation); - } - - public void addSprinklerData(SimpleLocation simpleLocation, Sprinkler sprinkler) { - sprinklerMap.put(simpleLocation, sprinkler); - } - - public void addWaterToPot(SimpleLocation simpleLocation, int amount, @NotNull String pot_id) { - Pot pot = potMap.get(simpleLocation); - if (pot != null) { - if (pot.addWater(amount)) { - changePotModel(simpleLocation, pot); - } - return; - } - Pot newPot = new Pot(pot_id, null, amount); - potMap.put(simpleLocation, newPot); - changePotModel(simpleLocation, newPot); - } - - public void addFertilizerToPot(SimpleLocation simpleLocation, Fertilizer fertilizer, @NotNull String pot_id) { - Pot pot = potMap.get(simpleLocation); - if (pot != null) { - pot.setFertilizer(fertilizer); - CustomCrops.getInstance().getScheduler().runTask(() -> changePotModel(simpleLocation, pot)); - } else { - Pot newPot = new Pot(pot_id, fertilizer, 0); - potMap.put(simpleLocation, newPot); - CustomCrops.getInstance().getScheduler().runTask(() -> changePotModel(simpleLocation, newPot)); - } - } - - public void scheduleGrowTask(CCWorld ccWorld, int force) { - Random randomGenerator = ThreadLocalRandom.current(); - int delay = force == -1 ? ConfigManager.pointGainInterval * 1000 : force * 1000; - for (SimpleLocation simpleLocation : growingCropMap.keySet()) { - ccWorld.pushCropTask(simpleLocation, randomGenerator.nextInt(delay)); - } - } - - public void scheduleSprinklerTask(CCWorld ccWorld, int force) { - Random randomGenerator = ThreadLocalRandom.current(); - int delay = force == -1 ? ConfigManager.pointGainInterval * 1000 : force * 1000; - delay = Math.max(delay - 10000, 10000); - for (SimpleLocation simpleLocation : sprinklerMap.keySet()) { - ccWorld.pushSprinklerTask(simpleLocation, randomGenerator.nextInt(delay)); - } - } - - public void scheduleConsumeTask(CCWorld ccWorld, int force) { - Random randomGenerator = ThreadLocalRandom.current(); - int delay = force == -1 ? ConfigManager.pointGainInterval * 1000 : force * 1000; - for (SimpleLocation simpleLocation : potMap.keySet()) { - ccWorld.pushConsumeTask(simpleLocation, randomGenerator.nextInt(delay)); - } - } - - public void changePotModel(SimpleLocation simpleLocation, Pot pot) { - Location location = simpleLocation.getBukkitLocation(); - if (location == null) return; - if (!CustomCrops.getInstance().getPlatformInterface().removeAnyBlock(location)) { - CustomCrops.getInstance().getWorldDataManager().removePotData(simpleLocation); - return; - } - String replacer = pot.isWet() ? pot.getConfig().getWetPot(pot.getFertilizer()) : pot.getConfig().getDryPot(pot.getFertilizer()); - if (ConfigUtils.isVanillaItem(replacer)) { - Block block = location.getBlock(); - block.setType(Material.valueOf(replacer)); - if (block.getBlockData() instanceof Farmland farmland && ConfigManager.disableMoistureMechanic) { - farmland.setMoisture(pot.isWet() ? farmland.getMaximumMoisture() : 0); - block.setBlockData(farmland); - } - } else { - CustomCrops.getInstance().getPlatformInterface().placeNoteBlock(location, replacer); - } - } - - public void executeReplaceTask() { - PlatformInterface platform = CustomCrops.getInstance().getPlatformInterface(); - for (Map.Entry entry : replaceTaskMap.entrySet()) { - SimpleLocation simpleLocation = entry.getKey(); - String id = entry.getValue().getId(); - if (id == null) { - platform.removeCustomItem(entry.getKey().getBukkitLocation(), entry.getValue().getItemMode()); - continue; - } - switch (entry.getValue().getItemType()) { - case POT -> { - Pot pot =getPotData(simpleLocation); - if (pot == null) { - String blockID = platform.getBlockID(simpleLocation.getBukkitLocation().getBlock()); - String potKey = CustomCrops.getInstance().getPotManager().getPotKeyByBlockID(blockID); - if (potKey == null) continue; - pot = new Pot(potKey, null, 0); - } - changePotModel(simpleLocation, pot); - } - case CROP -> { - Location location = simpleLocation.getBukkitLocation(); - if (platform.removeCustomItem(location, entry.getValue().getItemMode())) { - platform.placeCustomItem(location, id, entry.getValue().getItemMode()); - } - } - } - } - replaceTaskMap.clear(); - } - - public void addReplaceTask(SimpleLocation simpleLocation, OfflineReplaceTask offlineReplaceTask) { - replaceTaskMap.put(simpleLocation, offlineReplaceTask); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/CCWorld.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/world/CCWorld.java deleted file mode 100644 index 97c05b5..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/CCWorld.java +++ /dev/null @@ -1,994 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.world; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.ItemType; -import net.momirealms.customcrops.api.object.OfflineReplaceTask; -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.api.object.action.VariationImpl; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.condition.Condition; -import net.momirealms.customcrops.api.object.condition.DeathCondition; -import net.momirealms.customcrops.api.object.crop.CropConfig; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.crop.StageConfig; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.object.fertilizer.SoilRetain; -import net.momirealms.customcrops.api.object.fertilizer.SpeedGrow; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.pot.PotConfig; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.api.object.season.SeasonData; -import net.momirealms.customcrops.api.object.sprinkler.Sprinkler; -import net.momirealms.customcrops.api.object.sprinkler.SprinklerAnimation; -import net.momirealms.customcrops.api.object.sprinkler.SprinklerConfig; -import net.momirealms.customcrops.helper.Log; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import net.momirealms.customcrops.util.FakeEntityUtils; -import net.momirealms.customcrops.util.RotationUtils; -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.block.data.type.Farmland; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.util.*; -import java.util.concurrent.*; - -public class CCWorld extends Function { - - private final String worldName; - private final Reference world; - private final ConcurrentHashMap chunkMap; - private final ScheduledThreadPoolExecutor schedule; - private long currentDay; - private ScheduledFuture timerTask; - private int pointTimer; - private int cacheTimer; - private int workCounter; - private int consumeCounter; - private final Set plantInPoint; - private final Set loadInPoint; - private final ConcurrentHashMap corruptedPot; - private final File chunksFolder; - private final File dateFile; - private final File corruptedFile; - private final CustomCrops plugin; - - public CCWorld(World world, CustomCrops plugin) { - this.plugin = plugin; - this.worldName = world.getName(); - this.chunksFolder = ConfigUtils.getFile(world, "chunks"); - this.dateFile = ConfigUtils.getFile(world, "data.yml"); - this.corruptedFile = ConfigUtils.getFile(world, "corrupted.yml"); - this.world = new WeakReference<>(world); - this.chunkMap = new ConcurrentHashMap<>(64); - this.schedule = new ScheduledThreadPoolExecutor(ConfigManager.corePoolSize); - this.schedule.setMaximumPoolSize(ConfigManager.maxPoolSize); - this.schedule.setKeepAliveTime(ConfigManager.keepAliveTime, TimeUnit.SECONDS); - this.schedule.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); - this.plantInPoint = Collections.synchronizedSet(new HashSet<>(128)); - this.loadInPoint = Collections.synchronizedSet(new HashSet<>(32)); - this.corruptedPot = new ConcurrentHashMap<>(128); - this.cacheTimer = ConfigManager.cacheSaveInterval; - } - - @Override - @SuppressWarnings("ResultOfMethodCallIgnored") - public void init() { - loadDateData(); - loadCorruptedPots(); - if (!chunksFolder.exists()) chunksFolder.mkdirs(); - if (!ConfigManager.onlyInLoadedChunks) { - loadAllChunkData(); - } else { - for (Chunk chunk : Objects.requireNonNull(world.get()).getLoadedChunks()) { - loadChunk(ChunkCoordinate.getByBukkitChunk(chunk)); - } - } - } - - @Override - public void disable() { - closePool(); - saveDateData(); - saveCorruptedPots(); - saveAllChunkData(); - plugin.getSeasonManager().unloadSeasonData(worldName); - } - - public void load() { - this.pointTimer = ConfigManager.pointGainInterval; - this.cacheTimer = ConfigManager.cacheSaveInterval; - this.consumeCounter = ConfigManager.intervalConsume; - this.workCounter = ConfigManager.intervalWork; - this.scheduleTask(); - } - - public void unload() { - if (this.timerTask != null && !this.timerTask.isCancelled()) { - this.timerTask.cancel(false); - this.timerTask = null; - } - } - - public void loadCorruptedPots() { - YamlConfiguration dataFile = ConfigUtils.readData(corruptedFile); - for (Map.Entry entry : dataFile.getValues(false).entrySet()) { - corruptedPot.put(SimpleLocation.getByString(entry.getKey(), worldName), (String) entry.getValue()); - } - } - - public void saveCorruptedPots() { - YamlConfiguration dataFile = new YamlConfiguration(); - for (Map.Entry entry : corruptedPot.entrySet()) { - SimpleLocation simpleLocation = entry.getKey(); - dataFile.set(simpleLocation.getX() + "," + simpleLocation.getY() + "," + simpleLocation.getZ(), entry.getValue()); - } - try { - dataFile.save(corruptedFile); - } catch (IOException e) { - AdventureUtils.consoleMessage("[CustomCrops] Failed to save corrupted data for world: " + worldName); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - public void loadAllChunkData() { - File[] data_files = chunksFolder.listFiles(); - if (data_files == null) return; - List outdated = new ArrayList<>(); - for (File file : data_files) { - ChunkCoordinate chunkCoordinate = ChunkCoordinate.getByString(file.getName().substring(0, file.getName().length() - 7)); - try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) { - CCChunk chunk = (CCChunk) ois.readObject(); - if (chunk.isUseless()) { - outdated.add(file); - continue; - } - if (chunkCoordinate != null) chunkMap.put(chunkCoordinate, chunk); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - Log.info("Error at " + file.getAbsolutePath()); - outdated.add(file); - } - } - for (File file : outdated) { - file.delete(); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - public void saveAllChunkData() { - if (!chunksFolder.exists()) chunksFolder.mkdirs(); - for (Map.Entry entry : chunkMap.entrySet()) { - ChunkCoordinate chunkCoordinate = entry.getKey(); - CCChunk chunk = entry.getValue(); - String fileName = chunkCoordinate.getFileName() + ".ccdata"; - File file = new File(chunksFolder, fileName); - if (chunk.isUseless() && file.exists()) { - file.delete(); - continue; - } - try (FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos)) { - oos.writeObject(chunk); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - public void saveDateData() { - YamlConfiguration dataFile = new YamlConfiguration(); - if (ConfigManager.enableSeason && !ConfigManager.rsHook) { - SeasonData seasonData = plugin.getSeasonManager().getSeasonData(worldName); - if (seasonData == null) { - dataFile.set("season", "SPRING"); - dataFile.set("date", 1); - } else { - dataFile.set("season", seasonData.getSeason().name()); - dataFile.set("date", seasonData.getDate()); - } - } - dataFile.set("day", currentDay); - try { - dataFile.save(dateFile); - } catch (IOException e) { - AdventureUtils.consoleMessage("[CustomCrops] Failed to save season data for world: " + worldName); - } - } - - public void loadDateData() { - YamlConfiguration dataFile = ConfigUtils.readData(dateFile); - if (ConfigManager.enableSeason) { - SeasonData seasonData; - if (dataFile.contains("season") && dataFile.contains("date")) { - seasonData = new SeasonData(worldName, CCSeason.valueOf(dataFile.getString("season")), dataFile.getInt("date")); - } else { - seasonData = new SeasonData(worldName); - } - plugin.getSeasonManager().loadSeasonData(seasonData); - } - this.currentDay = dataFile.getLong("day", 0); - } - - private void scheduleTask() { - if (this.timerTask == null) { - this.timerTask = plugin.getScheduler().runTaskTimerAsync(() -> { - World worldInstance = world.get(); - if (worldInstance != null) { - if (ConfigManager.debugScheduler) { - Log.info("Queue size: " + schedule.getQueue().size() + " Completed: " + schedule.getCompletedTaskCount()); - } - long day = worldInstance.getFullTime() / 24000; - long time = worldInstance.getTime(); - this.tryDayCycleTask(time, day); - this.timerTask(); - } else { - AdventureUtils.consoleMessage("[CustomCrops] World: " + worldName + " unloaded unexpectedly. Shutdown the schedule."); - this.disable(); - } - }, 1000, 1000L); - } - } - - private void tryDayCycleTask(long time, long day) { - if (time < 100 && day != currentDay) { - currentDay = day; - if (ConfigManager.enableSeason && !ConfigManager.rsHook && ConfigManager.autoSeasonChange) { - plugin.getSeasonManager().addDate(worldName); - } - } - if (ConfigManager.cacheSaveInterval != -1) { - cacheTimer--; - if (cacheTimer <= 0) { - if (ConfigManager.debugScheduler) Log.info("== Save cache =="); - cacheTimer = ConfigManager.cacheSaveInterval; - schedule.execute(this::saveDateData); - schedule.execute(this::saveCorruptedPots); - schedule.execute(this::saveAllChunkData); - } - } - } - - private void timerTask() { - pointTimer--; - if (pointTimer <= 0) { - pointTimer = ConfigManager.pointGainInterval; - onReachPoint(); - } - } - - public void onReachPoint() { - if (ConfigManager.debugScheduler) Log.info("== Grow point =="); - if (ConfigManager.enableScheduleSystem) { - // clear the locations where crops are planted in a point interval - plantInPoint.clear(); - // log the chunk coordinates that has grown in a point interval - if (ConfigManager.onlyInLoadedChunks) { - loadInPoint.clear(); - loadInPoint.addAll(chunkMap.keySet()); - } - // clear the queue if there exists unhandled tasks - schedule.getQueue().clear(); - // arrange crop grow check task - for (CCChunk chunk : chunkMap.values()) { - chunk.scheduleGrowTask(this, -1); - } - workCounter--; - consumeCounter--; - if (consumeCounter == 0) { - if (ConfigManager.debugScheduler) Log.info("== Consume time =="); - consumeCounter = ConfigManager.intervalConsume; - scheduleConsumeTask(-1); - } - if (workCounter == 0) { - if (ConfigManager.debugScheduler) Log.info("== Work time =="); - workCounter = ConfigManager.intervalWork; - scheduleSprinklerWork(-1); - } - } - } - - private void closePool() { - this.schedule.shutdown(); - if (this.timerTask != null && !this.timerTask.isCancelled()) { - this.timerTask.cancel(false); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - public void unloadChunk(ChunkCoordinate chunkCoordinate) { - if (!ConfigManager.onlyInLoadedChunks) return; - CCChunk chunk = chunkMap.remove(chunkCoordinate); - if (chunk != null) { - File file = new File(chunksFolder, chunkCoordinate.getFileName() + ".ccdata"); - if (chunk.isUseless() && file.exists()) { - file.delete(); - return; - } - try (FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos)) { - oos.writeObject(chunk); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - public void loadChunk(ChunkCoordinate chunkCoordinate) { - if (ConfigManager.onlyInLoadedChunks) { - File file = new File(chunksFolder, chunkCoordinate.getFileName() + ".ccdata"); - if (file.exists()) { - boolean delete = false; - try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) { - CCChunk chunk = (CCChunk) ois.readObject(); - if (chunk.isUseless()) { - delete = true; - } else { - chunkMap.put(chunkCoordinate, chunk); - if (!loadInPoint.contains(chunkCoordinate)) { - chunk.scheduleGrowTask(this, -1); - } - } - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - Log.info("Error at " + file.getAbsolutePath()); - } finally { - if (delete) { - file.delete(); - } - } - } - return; - } - if (ConfigManager.updateDuringLoading) { - CCChunk chunk = chunkMap.get(chunkCoordinate); - if (chunk != null) { - chunk.executeReplaceTask(); - } - } - } - - public void pushCropTask(SimpleLocation simpleLocation, int delay) { - schedule.schedule(new CropCheckTask(simpleLocation), delay, TimeUnit.MILLISECONDS); - } - - public void pushSprinklerTask(SimpleLocation simpleLocation, int delay) { - schedule.schedule(new SprinklerCheckTask(simpleLocation), delay, TimeUnit.MILLISECONDS); - } - - public void pushConsumeTask(SimpleLocation simpleLocation, int delay) { - schedule.schedule(new ConsumeCheckTask(simpleLocation), delay, TimeUnit.MILLISECONDS); - } - - public String removeCorrupted(SimpleLocation simpleLocation) { - return corruptedPot.remove(simpleLocation); - } - - public void fixCorruptedData() { - for (SimpleLocation simpleLocation : corruptedPot.keySet()) { - CustomCrops.getInstance().getScheduler().runTaskAsyncLater(new FixTask(simpleLocation), ThreadLocalRandom.current().nextInt(30000)); - } - } - - public class FixTask implements Runnable { - - private final SimpleLocation simpleLocation; - - public FixTask(SimpleLocation simpleLocation) { - this.simpleLocation = simpleLocation; - } - - @Override - public void run() { - String key = corruptedPot.remove(simpleLocation); - PotConfig potConfig = plugin.getPotManager().getPotConfig(key); - if (potConfig == null) return; - Pot pot = getPotData(simpleLocation); - boolean wet = false; - Fertilizer fertilizer = null; - if (pot != null) { - wet = pot.isWet(); - fertilizer = pot.getFertilizer(); - } - Location location = simpleLocation.getBukkitLocation(); - if (location == null) return; - String replacer = wet ? potConfig.getWetPot(fertilizer) : potConfig.getDryPot(fertilizer); - CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); - asyncGetChunk.whenComplete((result, throwable) -> - plugin.getScheduler().runTask(() -> plugin.getPlatformInterface().placeNoteBlock(location, replacer) - )); - } - } - - public class ConsumeCheckTask implements Runnable { - - private final SimpleLocation simpleLocation; - - public ConsumeCheckTask(SimpleLocation simpleLocation) { - this.simpleLocation = simpleLocation; - } - - public void run() { - - Pot pot = getPotData(simpleLocation); - if (pot == null) return; - - if (pot.isWet() && plugin.getFertilizerManager().getConfigByFertilizer(pot.getFertilizer()) instanceof SoilRetain soilRetain && soilRetain.canTakeEffect()) { - pot.setWater(pot.getWater() + 1); - } - - if (pot.reduceWater() | pot.reduceFertilizer()) { - - Fertilizer fertilizer = pot.getFertilizer(); - boolean wet = pot.isWet(); - if (!wet && fertilizer == null && !ConfigManager.onlyInLoadedChunks) { - removePotData(simpleLocation); - } - - PotConfig potConfig = pot.getConfig(); - if (wet && fertilizer == null && !potConfig.enableFertilizedLooks()) { - return; - } - - if (world.get().isChunkLoaded(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4)) { - replacePot(simpleLocation, pot, potConfig); - return; - } - - if (ConfigManager.updateDuringLoading) { - addOfflineReplaceTask(simpleLocation, new OfflineReplaceTask(pot.getKey(), ItemType.POT, ItemMode.NOTE_BLOCK)); - return; - } - - Location location = simpleLocation.getBukkitLocation(); - if (location == null) { - return; - } - - CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); - asyncGetChunk.whenComplete((result, throwable) -> { - replacePot(simpleLocation, pot, potConfig); - }); - } - } - } - - private void replacePot(SimpleLocation simpleLocation, Pot pot, PotConfig potConfig) { - Location location = simpleLocation.getBukkitLocation(); - assert location != null; - plugin.getScheduler().runTask(() -> { - Block block = location.getBlock(); - if (block.getType() == Material.AIR) { - removePotData(simpleLocation); - return; - } - String replacer = pot.isWet() ? potConfig.getWetPot(pot.getFertilizer()) : potConfig.getDryPot(pot.getFertilizer()); - String id = plugin.getPlatformInterface().getBlockID(block); - if (ConfigManager.enableCorruptionFixer && id.equals("NOTE_BLOCK")) { - corruptedPot.put(simpleLocation, pot.getKey()); - if (ConfigManager.debugCorruption) AdventureUtils.consoleMessage("[CustomCrops] Corrupted pot found at: " + simpleLocation); - return; - } - String potKey = plugin.getPotManager().getPotKeyByBlockID(id); - if (potKey == null) { - removePotData(simpleLocation); - return; - } - if (!potKey.equals(pot.getKey())) { - return; - } - if (ConfigUtils.isVanillaItem(replacer)) { - block.setType(Material.valueOf(replacer)); - if (block.getBlockData() instanceof Farmland farmland && ConfigManager.disableMoistureMechanic) { - farmland.setMoisture(pot.isWet() ? farmland.getMaximumMoisture() : 0); - block.setBlockData(farmland); - } - } else { - plugin.getPlatformInterface().placeNoteBlock(location, replacer); - } - }); - } - - public class SprinklerCheckTask implements Runnable { - - private final SimpleLocation simpleLocation; - - public SprinklerCheckTask(SimpleLocation simpleLocation) { - this.simpleLocation = simpleLocation; - } - - public void run() { - Sprinkler sprinkler = getSprinklerData(simpleLocation); - if (sprinkler == null) return; - - SprinklerConfig sprinklerConfig = sprinkler.getConfig(); - if (sprinklerConfig == null) { - removeSprinklerData(simpleLocation); - return; - } - - if (!sprinklerConfig.isInfinite()) { - int water = sprinkler.getWater(); - sprinkler.setWater(--water); - if (water <= 0) { - removeSprinklerData(simpleLocation); - } - } - - SprinklerAnimation sprinklerAnimation = sprinklerConfig.getSprinklerAnimation(); - - if (world.get().isChunkLoaded(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4) && sprinklerAnimation != null) { - for (Player player : Bukkit.getOnlinePlayers()) { - SimpleLocation playerLoc = SimpleLocation.getByBukkitLocation(player.getLocation()); - if (playerLoc.isNear(simpleLocation, 48)) { - FakeEntityUtils.playWaterAnimation(player, simpleLocation.getBukkitLocation().add(0.5, sprinklerAnimation.offset(), 0.5), sprinklerAnimation.id(), sprinklerAnimation.duration(), sprinklerAnimation.itemMode()); - } - } - } - - int range = sprinklerConfig.getRange(); - int amount = sprinklerConfig.getWaterFillAbility(); - int random = sprinklerAnimation == null ? 10000 : sprinklerAnimation.duration() * 1000; - String[] whiteList = sprinklerConfig.getPotWhitelist(); - for (int i = -range; i <= range; i++) { - for (int j = -range; j <= range; j++) { - SimpleLocation potSLoc = simpleLocation.add(i, -1, j); - schedule.schedule(new WaterPotTask(potSLoc, amount, whiteList), ThreadLocalRandom.current().nextInt(random), TimeUnit.MILLISECONDS); - } - } - } - } - - public class WaterPotTask implements Runnable { - - @NotNull - private final SimpleLocation simpleLocation; - private final int amount; - @Nullable - private final String[] whitelist; - - public WaterPotTask(@NotNull SimpleLocation simpleLocation, int amount, @Nullable String[] whitelist) { - this.simpleLocation = simpleLocation; - this.amount = amount; - this.whitelist = whitelist; - } - - @Override - public void run() { - - if (world.get().isChunkLoaded(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4)) { - replacePot(simpleLocation, amount, whitelist); - return; - } - - Pot pot = getPotData(simpleLocation); - if (ConfigManager.updateDuringLoading && pot != null) { - pot.addWater(amount); - addOfflineReplaceTask(simpleLocation, new OfflineReplaceTask("unknown", ItemType.POT, ItemMode.NOTE_BLOCK)); - return; - } - - CompletableFuture asyncGetChunk = world.get().getChunkAtAsync(simpleLocation.getX() >> 4, simpleLocation.getZ() >> 4); - asyncGetChunk.whenComplete((result, throwable) -> replacePot(simpleLocation, amount, whitelist)); - } - } - - private void replacePot(SimpleLocation simpleLocation, int amount, String[] whitelist) { - Location location = simpleLocation.getBukkitLocation(); - assert location != null; - plugin.getScheduler().runTask(() -> { - String blockID = plugin.getPlatformInterface().getBlockID(location.getBlock()); - String potKey = plugin.getPotManager().getPotKeyByBlockID(blockID); - if (potKey != null) { - if (whitelist != null) { - for (String pot : whitelist) { - if (pot.equals(potKey)) { - addWaterToPot(simpleLocation, amount, potKey); - break; - } - } - } else { - addWaterToPot(simpleLocation, amount, potKey); - } - } else if (ConfigManager.enableCorruptionFixer && blockID.equals("NOTE_BLOCK")) { - Pot pot = getPotData(simpleLocation); - if (pot != null) { - potKey = pot.getKey(); - if (whitelist == null) { - pot.addWater(amount); - } else { - for (String potID : whitelist) { - if (potID.equals(potKey)) { - pot.addWater(amount); - break; - } - } - } - corruptedPot.put(simpleLocation, potKey); - if (ConfigManager.debugCorruption) AdventureUtils.consoleMessage("[CustomCrops] Corrupted pot found at: " + simpleLocation); - } - } - }); - } - - public class CropCheckTask implements Runnable { - - private final SimpleLocation simpleLocation; - - public CropCheckTask(SimpleLocation simpleLocation) { - this.simpleLocation = simpleLocation; - } - - public void run() { - GrowingCrop growingCrop = getCropData(simpleLocation); - if (growingCrop == null) return; - - CropConfig cropConfig = growingCrop.getConfig(); - if (cropConfig == null) { - removeCropData(simpleLocation); - return; - } - - ItemMode itemMode = cropConfig.getCropMode(); - DeathCondition[] deathConditions = cropConfig.getDeathConditions(); - if (deathConditions != null) { - for (DeathCondition deathCondition : deathConditions) { - if (deathCondition.checkIfDead(simpleLocation)) { - removeCropData(simpleLocation); - deathCondition.applyDeadModel(simpleLocation, itemMode); - return; - } - } - } - - Condition[] conditions = cropConfig.getGrowConditions(); - if (conditions != null) { - for (Condition condition : conditions) { - if (!condition.isMet(simpleLocation)) { - return; - } - } - } - - int points = 1; - Pot pot = getPotData(simpleLocation.add(0,-1,0)); - if (pot != null) { - FertilizerConfig fertilizerConfig = plugin.getFertilizerManager().getConfigByFertilizer(pot.getFertilizer()); - if (fertilizerConfig instanceof SpeedGrow speedGrow) { - points += speedGrow.getPointBonus(); - } - } - addCropPoint(points, cropConfig, growingCrop, simpleLocation, itemMode); - } - } - - public boolean addCropPointAt(SimpleLocation simpleLocation, int points) { - GrowingCrop growingCrop = getCropData(simpleLocation); - if (growingCrop == null) return false; - CropConfig cropConfig = growingCrop.getConfig(); - if (cropConfig == null) { - removeCropData(simpleLocation); - return false; - } - if (points == 0) return true; - addCropPoint(points, cropConfig, growingCrop, simpleLocation, cropConfig.getCropMode()); - return true; - } - - public void addCropPoint(int points, CropConfig cropConfig, GrowingCrop growingCrop, SimpleLocation simpleLocation, ItemMode itemMode) { - int current = growingCrop.getPoints(); - String nextModel = null; - for (int i = current + 1; i <= points + current; i++) { - StageConfig stageConfig = cropConfig.getStageConfig(i); - if (stageConfig == null) continue; - if (stageConfig.getModel() != null) nextModel = stageConfig.getModel(); - Action[] growActions = stageConfig.getGrowActions(); - if (growActions != null) { - for (Action action : growActions) { - if (action instanceof VariationImpl variation) { - if (variation.doOn(simpleLocation, itemMode)) { - return; - } - } else { - action.doOn(null, simpleLocation, itemMode); - } - } - } - } - - growingCrop.setPoints(current + points); - if (growingCrop.getPoints() >= cropConfig.getMaxPoints()) { - removeCropData(simpleLocation); - } - - Location location = simpleLocation.getBukkitLocation(); - String finalNextModel = nextModel; - if (finalNextModel == null || location == null) return; - - if (world.get().isChunkLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4)) { - switch (itemMode) { - case ITEM_FRAME -> replaceItemFrameCrop(location, finalNextModel, cropConfig.isRotationEnabled()); - case ITEM_DISPLAY -> replaceItemDisplayCrop(location, finalNextModel, cropConfig.isRotationEnabled()); - case TRIPWIRE -> replaceTripwireCrop(location, finalNextModel); - } - return; - } - - if (ConfigManager.updateDuringLoading) { - addOfflineReplaceTask(simpleLocation, new OfflineReplaceTask(finalNextModel, ItemType.CROP, itemMode)); - return; - } - - CompletableFuture asyncGetChunk = location.getWorld().getChunkAtAsync(location.getBlockX() >> 4, location.getBlockZ() >> 4); - if (itemMode == ItemMode.ITEM_FRAME) { - CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { - chunk.getEntities(); - return chunk.isEntitiesLoaded(); - }); - loadEntities.whenComplete((result, throwable) -> replaceItemFrameCrop(location, finalNextModel, cropConfig.isRotationEnabled())); - } else if (itemMode == ItemMode.ITEM_DISPLAY) { - CompletableFuture loadEntities = asyncGetChunk.thenApply((chunk) -> { - chunk.getEntities(); - return chunk.isEntitiesLoaded(); - }); - loadEntities.whenComplete((result, throwable) -> replaceItemDisplayCrop(location, finalNextModel, cropConfig.isRotationEnabled())); - } else { - asyncGetChunk.whenComplete((result, throwable) -> replaceTripwireCrop(location, finalNextModel)); - } - } - - private void replaceItemFrameCrop(Location location, String model, boolean rotation) { - plugin.getScheduler().runTask(() -> { - if (plugin.getPlatformInterface().removeCustomItem(location, ItemMode.ITEM_FRAME)) { - ItemFrame itemFrame = plugin.getPlatformInterface().placeItemFrame(location, model); - if (itemFrame != null && rotation) itemFrame.setRotation(RotationUtils.getRandomRotation()); - } else { - removeCropData(SimpleLocation.getByBukkitLocation(location)); - } - }); - } - - private void replaceItemDisplayCrop(Location location, String model, boolean rotation) { - plugin.getScheduler().runTask(() -> { - if (plugin.getPlatformInterface().removeCustomItem(location, ItemMode.ITEM_DISPLAY)) { - ItemDisplay itemDisplay = plugin.getPlatformInterface().placeItemDisplay(location, model); - if (itemDisplay != null && rotation) itemDisplay.setRotation(RotationUtils.getRandomFloatRotation(), itemDisplay.getLocation().getPitch()); - } else { - removeCropData(SimpleLocation.getByBukkitLocation(location)); - } - }); - } - - private void replaceTripwireCrop(Location location, String model) { - plugin.getScheduler().runTask(() -> { - if (plugin.getPlatformInterface().removeCustomItem(location, ItemMode.TRIPWIRE)) { - plugin.getPlatformInterface().placeTripWire(location, model); - } else { - removeCropData(SimpleLocation.getByBukkitLocation(location)); - } - }); - } - - public void removePotData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removePotData(simpleLocation); - } - - public void removeCropData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removeCropData(simpleLocation); - } - - public void addCropData(SimpleLocation simpleLocation, GrowingCrop growingCrop, boolean grow) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addCropData(simpleLocation, growingCrop); - if (grow) growIfNotDuplicated(simpleLocation); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addCropData(simpleLocation, growingCrop); - if (grow) growIfNotDuplicated(simpleLocation); - } - - private void growIfNotDuplicated(SimpleLocation simpleLocation) { - if (plantInPoint.contains(simpleLocation)) { - return; - } - pushCropTask(simpleLocation, ThreadLocalRandom.current().nextInt(ConfigManager.pointGainInterval * 1000)); - plantInPoint.add(simpleLocation); - } - - public GrowingCrop getCropData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - return chunk.getCropData(simpleLocation); - } - return null; - } - - public int getChunkCropAmount(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return 0; - return chunk.getCropAmount(); - } - - public void removeGreenhouse(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removeGreenhouse(simpleLocation); - } - - public void addGreenhouse(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addGreenhouse(simpleLocation); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addGreenhouse(simpleLocation); - } - - public boolean isGreenhouse(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return false; - return chunk.isGreenhouse(simpleLocation); - } - - public void removeScarecrow(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removeScarecrow(simpleLocation); - } - - public void addScarecrow(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addScarecrow(simpleLocation); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addScarecrow(simpleLocation); - } - - public boolean hasScarecrow(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return false; - return chunk.hasScarecrow(); - } - - public void removeSprinklerData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return; - chunk.removeSprinklerData(simpleLocation); - } - - public void addSprinklerData(SimpleLocation simpleLocation, Sprinkler sprinkler) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addSprinklerData(simpleLocation, sprinkler); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addSprinklerData(simpleLocation, sprinkler); - } - - @Nullable - public Sprinkler getSprinklerData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return null; - return chunk.getSprinklerData(simpleLocation); - } - - public void addWaterToPot(SimpleLocation simpleLocation, int amount, @NotNull String pot_id) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addWaterToPot(simpleLocation, amount, pot_id); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addWaterToPot(simpleLocation, amount, pot_id); - } - - public void addFertilizerToPot(SimpleLocation simpleLocation, Fertilizer fertilizer, @NotNull String pot_id) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addFertilizerToPot(simpleLocation, fertilizer, pot_id); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addFertilizerToPot(simpleLocation, fertilizer, pot_id); - } - - public Pot getPotData(SimpleLocation simpleLocation) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk == null) return null; - return chunk.getPotData(simpleLocation); - } - - public void addPotData(SimpleLocation simpleLocation, Pot pot) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addPotData(simpleLocation, pot); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addPotData(simpleLocation, pot); - } - - public CCChunk createNewChunk(SimpleLocation simpleLocation) { - ChunkCoordinate chunkCoordinate = simpleLocation.getChunkCoordinate(); - CCChunk newChunk = new CCChunk(); - chunkMap.put(chunkCoordinate, newChunk); - return newChunk; - } - - public void scheduleSprinklerWork(int force) { - schedule.execute(() -> { - for (CCChunk chunk : chunkMap.values()) { - chunk.scheduleSprinklerTask(this, force); - } - }); - } - - public void scheduleConsumeTask(int force) { - schedule.execute(() -> { - for (CCChunk chunk : chunkMap.values()) { - chunk.scheduleConsumeTask(this, force); - } - }); - } - - public void scheduleCropGrowTask(int force) { - schedule.execute(() -> { - for (CCChunk chunk : chunkMap.values()) { - chunk.scheduleGrowTask(this, force); - } - }); - } - - public void addOfflineReplaceTask(SimpleLocation simpleLocation, OfflineReplaceTask offlineReplaceTask) { - CCChunk chunk = chunkMap.get(simpleLocation.getChunkCoordinate()); - if (chunk != null) { - chunk.addReplaceTask(simpleLocation, offlineReplaceTask); - return; - } - chunk = createNewChunk(simpleLocation); - chunk.addReplaceTask(simpleLocation, offlineReplaceTask); - } - - @Nullable - public String getCorruptedPotOriginalKey(SimpleLocation simpleLocation) { - return corruptedPot.get(simpleLocation); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/SlimeWorldListener.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/world/SlimeWorldListener.java deleted file mode 100644 index aa4a1db..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/SlimeWorldListener.java +++ /dev/null @@ -1,41 +0,0 @@ -///* -// * Copyright (C) <2022> -// * -// * 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 -// * 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 . -// */ -// -//package net.momirealms.customcrops.api.object.world; -// -//import com.infernalsuite.aswm.api.events.LoadSlimeWorldEvent; -//import org.bukkit.Bukkit; -//import org.bukkit.World; -//import org.bukkit.event.EventHandler; -//import org.bukkit.event.Listener; -// -//public class SlimeWorldListener implements Listener { -// -// private final WorldDataManager worldDataManager; -// -// public SlimeWorldListener(WorldDataManager worldDataManager) { -// this.worldDataManager = worldDataManager; -// } -// -// @EventHandler -// public void onWorldLoad(LoadSlimeWorldEvent event) { -// World world = Bukkit.getWorld(event.getSlimeWorld().getName()); -// if (world != null) { -// worldDataManager.loadWorld(world); -// } -// } -//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/WorldDataManager.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/world/WorldDataManager.java deleted file mode 100644 index d417548..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/WorldDataManager.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.world; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.OfflineReplaceTask; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.sprinkler.Sprinkler; -import net.momirealms.customcrops.api.object.sprinkler.SprinklerConfig; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.World; -import org.bukkit.event.HandlerList; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.ConcurrentHashMap; - -public class WorldDataManager extends Function { - - private final ConcurrentHashMap worldMap; - private final CustomCrops plugin; - private final WorldListener worldListener; - - public WorldDataManager(CustomCrops plugin) { - this.plugin = plugin; - this.worldMap = new ConcurrentHashMap<>(); - this.worldListener = new WorldListener(this); - } - - @Override - public void load() { - Bukkit.getPluginManager().registerEvents(worldListener, plugin); - for (CCWorld ccWorld : worldMap.values()) { - ccWorld.load(); - } - } - - @Override - public void unload() { - HandlerList.unregisterAll(worldListener); - for (CCWorld ccWorld : worldMap.values()) { - ccWorld.unload(); - } - } - - @Override - public void disable() { - this.unload(); - for (CCWorld ccWorld : worldMap.values()) { - ccWorld.disable(); - } - this.worldMap.clear(); - } - - public void loadWorld(World world) { - if (ConfigManager.debugWorld) AdventureUtils.consoleMessage("World " + world.getName() + " is trying to load"); - if (!isWorldAllowed(world)) return; - CCWorld ccWorld = new CCWorld(world, plugin); - ccWorld.init(); - ccWorld.load(); - ccWorld.onReachPoint(); - worldMap.put(world.getName(), ccWorld); - if (ConfigManager.debugWorld) AdventureUtils.consoleMessage("World " + world.getName() + " is loaded"); - } - - public void unloadWorld(World world) { - CCWorld ccWorld = worldMap.remove(world.getName()); - if (ccWorld != null) { - ccWorld.disable(); - if (ConfigManager.debugWorld) AdventureUtils.consoleMessage("World " + world.getName() + " is unloaded"); - } - } - - public void removeCropData(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.removeCropData(simpleLocation); - } - } - - public void addCropData(SimpleLocation simpleLocation, GrowingCrop growingCrop, boolean grow) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.addCropData(simpleLocation, growingCrop, grow); - } - } - - public int getChunkCropAmount(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.getChunkCropAmount(simpleLocation); - } - return -1; - } - - public void removeGreenhouse(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.removeGreenhouse(simpleLocation); - } - } - - public void addGreenhouse(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.addGreenhouse(simpleLocation); - } - } - - public boolean isGreenhouse(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.isGreenhouse(simpleLocation); - } - return false; - } - - public boolean isWorldAllowed(World world) { - return ConfigManager.whiteListWorlds == ConfigManager.worldList.contains(world.getName()); - } - - public void removePotData(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.removePotData(simpleLocation); - } - } - - public void removeSprinklerData(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.removeSprinklerData(simpleLocation); - } - } - - @Nullable - public Sprinkler getSprinklerData(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.getSprinklerData(simpleLocation); - } - return null; - } - - public void addSprinklerData(SimpleLocation simpleLocation, Sprinkler sprinkler) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.addSprinklerData(simpleLocation, sprinkler); - } - } - - public void addScarecrow(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.addScarecrow(simpleLocation); - } - } - - public void removeScarecrow(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.removeScarecrow(simpleLocation); - } - } - - public boolean hasScarecrow(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.hasScarecrow(simpleLocation); - } - return false; - } - - public void addWaterToPot(SimpleLocation simpleLocation, int amount, String pot_id) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.addWaterToPot(simpleLocation, amount, pot_id); - } - } - - public void addFertilizerToPot(SimpleLocation simpleLocation, Fertilizer fertilizer, String pot_id) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.addFertilizerToPot(simpleLocation, fertilizer, pot_id); - } - } - - @Nullable - public Pot getPotData(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.getPotData(simpleLocation); - } - return null; - } - - public void addPotData(SimpleLocation simpleLocation, Pot pot) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.addPotData(simpleLocation, pot); - } - } - - public void addOfflineTask(SimpleLocation simpleLocation, OfflineReplaceTask offlineReplaceTask) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - ccWorld.addOfflineReplaceTask(simpleLocation, offlineReplaceTask); - } - } - - public void addWaterToSprinkler(SimpleLocation simpleLocation, int add, SprinklerConfig sprinklerConfig) { - Sprinkler sprinkler = getSprinklerData(simpleLocation); - if (sprinkler != null) { - sprinkler.setWater(Math.min(add + sprinkler.getWater(), sprinklerConfig.getStorage())); - } else { - Sprinkler newSprinkler = new Sprinkler(sprinklerConfig.getKey(), Math.min(add, sprinklerConfig.getStorage())); - addSprinklerData(simpleLocation, newSprinkler); - } - } - - public boolean addCropPointAt(SimpleLocation simpleLocation, int points) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.addCropPointAt(simpleLocation, points); - } - return false; - } - - @Nullable - public GrowingCrop getCropData(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.getCropData(simpleLocation); - } - return null; - } - - @Nullable - public CCWorld getWorld(String world) { - return worldMap.get(world); - } - - public void loadChunk(Chunk chunk, World world) { - CCWorld ccWorld = worldMap.get(world.getName()); - if (ccWorld != null) { - ccWorld.loadChunk(new ChunkCoordinate(chunk.getX(), chunk.getZ())); - } - } - - public void unloadChunk(Chunk chunk, World world) { - CCWorld ccWorld = worldMap.get(world.getName()); - if (ccWorld != null) { - ccWorld.unloadChunk(new ChunkCoordinate(chunk.getX(), chunk.getZ())); - } - } - - @Nullable - public String getCorruptedPotOriginalKey(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.getCorruptedPotOriginalKey(simpleLocation); - } - return null; - } - - public String removeCorrupted(SimpleLocation simpleLocation) { - CCWorld ccWorld = worldMap.get(simpleLocation.getWorldName()); - if (ccWorld != null) { - return ccWorld.removeCorrupted(simpleLocation); - } - return null; - } - - public void fixCorruptedData(String world) { - CCWorld ccWorld = worldMap.get(world); - if (ccWorld != null) { - ccWorld.fixCorruptedData(); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/WorldListener.java b/plugin/src/main/java/net/momirealms/customcrops/api/object/world/WorldListener.java deleted file mode 100644 index 2a88caf..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/world/WorldListener.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.api.object.world; - -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkLoadEvent; -import org.bukkit.event.world.ChunkUnloadEvent; -import org.bukkit.event.world.WorldLoadEvent; -import org.bukkit.event.world.WorldUnloadEvent; - -public class WorldListener implements Listener { - - private final WorldDataManager worldManager; - - public WorldListener(WorldDataManager worldManager) { - this.worldManager = worldManager; - } - - @EventHandler - public void onWorldLoad(WorldLoadEvent event) { - worldManager.loadWorld(event.getWorld()); - } - - @EventHandler - public void onWorldUnload(WorldUnloadEvent event) { - worldManager.unloadWorld(event.getWorld()); - } - - @EventHandler - public void onChunkLoad(ChunkLoadEvent event) { - if (event.isNewChunk()) return; - worldManager.loadChunk(event.getChunk(), event.getWorld()); - } - - @EventHandler - public void onChunkUnload(ChunkUnloadEvent event) { - worldManager.unloadChunk(event.getChunk(), event.getWorld()); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/AbstractMainCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/AbstractMainCommand.java deleted file mode 100644 index 4aa8a4e..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/AbstractMainCommand.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command; - -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabExecutor; -import org.jetbrains.annotations.NotNull; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -public abstract class AbstractMainCommand implements TabExecutor { - - protected final Map subCommandMap; - - public AbstractMainCommand() { - this.subCommandMap = new ConcurrentHashMap<>(); - } - - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - List argList = Arrays.asList(args); - if (argList.size() < 1) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.nonArgs); - return true; - } - if (!sender.hasPermission("customcrops." + argList.get(0))) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.noPerm); - return true; - } - AbstractSubCommand subCommand = subCommandMap.get(argList.get(0)); - if (subCommand != null) - return subCommand.onCommand(sender, argList.subList(1, argList.size())); - else { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.unavailableArgs); - return true; - } - } - - @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { - List argList = Arrays.asList(args); - if (argList.size() <= 1) { - List returnList = new ArrayList<>(subCommandMap.keySet()); - returnList.removeIf(str -> !str.startsWith(args[0]) || !sender.hasPermission("customcrops." + str)); - return returnList; - } - AbstractSubCommand subCommand = subCommandMap.get(argList.get(0)); - if (subCommand != null) - return subCommand.onTabComplete(sender, argList.subList(1, argList.size())); - else - return Collections.singletonList(""); - } - - public void regSubCommand(AbstractSubCommand executor) { - subCommandMap.put(executor.getSubCommand(), executor); - } - - public Map getSubCommandMap() { - return subCommandMap; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/AbstractSubCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/AbstractSubCommand.java deleted file mode 100644 index b8760ba..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/AbstractSubCommand.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command; - -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -public abstract class AbstractSubCommand { - - private final String command; - private Map subCommandMap; - - public AbstractSubCommand(String command) { - this.command = command; - } - - public boolean onCommand(CommandSender sender, List args) { - if (subCommandMap == null || args.size() < 1) { - return true; - } - AbstractSubCommand subCommand = subCommandMap.get(args.get(0)); - if (subCommand == null) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.unavailableArgs); - } else { - subCommand.onCommand(sender, args.subList(1, args.size())); - } - return true; - } - - public List onTabComplete(CommandSender sender, List args) { - if (subCommandMap == null) - return Collections.singletonList(""); - if (args.size() <= 1) { - List returnList = new ArrayList<>(subCommandMap.keySet()); - returnList.removeIf(str -> !str.startsWith(args.get(0))); - return returnList; - } - AbstractSubCommand subCmd = subCommandMap.get(args.get(0)); - if (subCmd != null) - return subCommandMap.get(args.get(0)).onTabComplete(sender, args.subList(1, args.size())); - return Collections.singletonList(""); - } - - public String getSubCommand() { - return command; - } - - public Map getSubCommands() { - return Collections.unmodifiableMap(subCommandMap); - } - - public void regSubCommand(AbstractSubCommand command) { - if (subCommandMap == null) { - subCommandMap = new ConcurrentHashMap<>(); - } - subCommandMap.put(command.getSubCommand(), command); - } - - protected boolean noConsoleExecute(CommandSender commandSender) { - if (!(commandSender instanceof Player)) { - AdventureUtils.consoleMessage(MessageManager.prefix + MessageManager.noConsole); - return true; - } - return false; - } - - protected boolean playerNotOnline(CommandSender commandSender, String player) { - if (Bukkit.getPlayer(player) == null) { - AdventureUtils.sendMessage(commandSender, MessageManager.prefix + MessageManager.notOnline.replace("{Player}", player)); - return true; - } - return false; - } - - protected boolean lackArgs(CommandSender commandSender, int required, int current) { - if (required > current) { - AdventureUtils.sendMessage(commandSender, MessageManager.prefix + MessageManager.lackArgs); - return true; - } - return false; - } - - protected List online_players() { - List online = new ArrayList<>(); - Bukkit.getOnlinePlayers().forEach((player -> online.add(player.getName()))); - return online; - } - - protected List filterStartingWith(List list, String prefix) { - return list.stream().filter(s -> s.startsWith(prefix)).collect(Collectors.toList()); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/CustomCropsCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/CustomCropsCommand.java deleted file mode 100644 index 66eabfe..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/CustomCropsCommand.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command; - -import net.momirealms.customcrops.command.subcmd.*; - -public class CustomCropsCommand extends AbstractMainCommand { - - public CustomCropsCommand() { - regSubCommand(ReloadCommand.INSTANCE); - regSubCommand(SetSeasonCommand.INSTANCE); - regSubCommand(HelpCommand.INSTANCE); - regSubCommand(AboutCommand.INSTANCE); - regSubCommand(SetDateCommand.INSTANCE); - regSubCommand(ForceCommand.INSTANCE); - regSubCommand(MigrateCommand.INSTANCE); - regSubCommand(ConvertCommand.INSTANCE); - regSubCommand(FixCommand.INSTANCE); -// regSubCommand(Test.INSTANCE); -// regSubCommand(PerformanceTest.INSTANCE); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/AboutCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/AboutCommand.java deleted file mode 100644 index f57d234..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/AboutCommand.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.command.CommandSender; - -import java.util.List; - -public class AboutCommand extends AbstractSubCommand { - - public static final AboutCommand INSTANCE = new AboutCommand(); - - public AboutCommand() { - super("about"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - AdventureUtils.sendMessage(sender, "<#FFA500>⛈ CustomCrops - <#F4A460>" + CustomCrops.getInstance().getVersionHelper().getPluginVersion()); - AdventureUtils.sendMessage(sender, "<#FFFFE0>Custom crop system based on thread pool"); - AdventureUtils.sendMessage(sender, "<#DA70D6>\uD83E\uDDEA Author: <#FFC0CB>XiaoMoMi"); - AdventureUtils.sendMessage(sender, "<#FF7F50>\uD83D\uDD25 Contributors: <#FFA07A>Cha_Shao, <#FFA07A>TopOrigin, <#FFA07A>AmazingCat"); - AdventureUtils.sendMessage(sender, "<#FFD700>⭐ Document <#A9A9A9>| <#FAFAD2>⛏ Github <#A9A9A9>| <#48D1CC>\uD83D\uDD14 Polymart"); - return true; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ConvertCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ConvertCommand.java deleted file mode 100644 index dcb5145..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ConvertCommand.java +++ /dev/null @@ -1,184 +0,0 @@ -package net.momirealms.customcrops.command.subcmd; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; - -public class ConvertCommand extends AbstractSubCommand { - - public static final ConvertCommand INSTANCE = new ConvertCommand(); - - private final HashSet confirm; - - public ConvertCommand() { - super("convert"); - confirm = new HashSet<>(); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - if (lackArgs(sender, 1, args.size())) return true; - - if (!confirm.contains(sender.getName())) { - confirm.add(sender.getName()); - AdventureUtils.sendMessage(sender, "[CustomCrops] Type the command again to confirm."); - return true; - } - - confirm.remove(sender.getName()); - - String mode = args.get(0).toUpperCase(Locale.ENGLISH); - - String platform = CustomCrops.getInstance().getPlatform().name().toLowerCase(); - YamlConfiguration oldConfig = YamlConfiguration.loadConfiguration(new File(CustomCrops.getInstance().getDataFolder(), "crops_" + platform + ".yml")); - String namespace = oldConfig.getString("namespace", ""); - if (!namespace.equals("")) namespace = namespace + ":"; - - for (String crop : oldConfig.getKeys(false)) { - - ConfigurationSection oldSection = oldConfig.getConfigurationSection(crop); - if (oldSection == null) continue; - int max_stage = oldSection.getInt("max-stage"); - - YamlConfiguration newConfig = new YamlConfiguration(); - ConfigurationSection newSection = newConfig.createSection(crop); - newSection.set("type", mode); - newSection.set("pot-whitelist", List.of("default")); - newSection.set("seed", namespace + crop + "_seeds"); - newSection.set("max-points", max_stage -1); - - ConfigurationSection pointSec = newSection.createSection("points"); - for (int i = 0; i < max_stage; i++) { - pointSec.set(i + ".model", namespace + crop + "_stage_" + (i+1)); - } - - if (oldSection.contains("gigantic-crop")) { - newSection.set("max-points", max_stage); - boolean isBlock = oldSection.contains("gigantic-crop.block"); - newSection.set("points." + max_stage + ".events.grow.action_gigantic.type", "variation"); - newSection.set("points." + max_stage + ".events.grow.action_gigantic.value.gigantic.item", isBlock ? oldSection.getString("gigantic-crop.block") : oldSection.getString("gigantic-crop.furniture")); - newSection.set("points." + max_stage + ".events.grow.action_gigantic.value.gigantic.type", isBlock ? "TRIPWIRE" : "ITEM_FRAME"); - newSection.set("points." + max_stage + ".events.grow.action_gigantic.value.gigantic.chance", oldSection.getDouble("gigantic-crop.chance")); - } - - if (oldSection.contains("return")) { - newSection.set("points." + (max_stage-1) + ".events.interact-by-hand.action_break.type", "break"); - newSection.set("points." + (max_stage-1) + ".events.interact-by-hand.action_break.value", true); - newSection.set("points." + (max_stage-1) + ".events.interact-by-hand.action_replant.type", "replant"); - newSection.set("points." + (max_stage-1) + ".events.interact-by-hand.action_replant.value.point", Integer.parseInt(oldSection.getString("return", "1").substring(oldSection.getString("return", "1").length()-1)) - 1); - newSection.set("points." + (max_stage-1) + ".events.interact-by-hand.action_replant.value.crop", crop); - newSection.set("points." + (max_stage-1) + ".events.interact-by-hand.action_replant.value.model", oldSection.getString("return")); - } - - if (oldSection.contains("season")) { - List allSeason = new java.util.ArrayList<>(List.of("spring", "autumn", "summer", "winter")); - for (String allow : oldSection.getStringList("season")) { - allSeason.remove(allow.toLowerCase()); - } - newSection.set("requirements.plant.requirement_season.type", "season"); - newSection.set("requirements.plant.requirement_season.value", oldSection.getStringList("season")); - newSection.set("requirements.plant.requirement_season.message", "It's not a good season to plant " + crop); - - newSection.set("death-conditions.unsuitable_season.model", namespace + "crop_stage_death"); - newSection.set("death-conditions.unsuitable_season.conditions.condition_season.type", "unsuitable_season"); - newSection.set("death-conditions.unsuitable_season.conditions.condition_season.value", allSeason); - } - - if (oldSection.contains("quality-loots")) { - ConfigurationSection qualitySec = oldSection.getConfigurationSection("quality-loots"); - assert qualitySec != null; - String[] split = qualitySec.getString("amount").split("~"); - int min = Integer.parseInt(split[0]); - int max = Integer.parseInt(split[1]); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.type", "drop-items"); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.quality-crops.min", min); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.quality-crops.max", max); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.quality-crops.items.1", qualitySec.getString("quality.1")); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.quality-crops.items.2", qualitySec.getString("quality.2")); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.quality-crops.items.3", qualitySec.getString("quality.3")); - } - - if (oldSection.contains("other-loots")) { - ConfigurationSection lootSec = oldSection.getConfigurationSection("other-loots"); - assert lootSec != null; - for (String loot_key : lootSec.getKeys(false)) { - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.other-items." + loot_key + ".item", lootSec.getString(loot_key + ".item")); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.other-items." + loot_key + ".min", lootSec.getInt(loot_key + ".min_amount")); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.other-items." + loot_key + ".max", lootSec.getInt(loot_key + ".max_amount")); - newSection.set("points." + (max_stage-1) + ".events.break.action_drop.value.other-items." + loot_key + ".chance", lootSec.getDouble(loot_key + ".chance")); - } - } - - if (oldSection.contains("harvest-actions.messages")) { - newSection.set("points." + (max_stage-1) + ".events.break.action_message.type", "message"); - newSection.set("points." + (max_stage-1) + ".events.break.action_message.value", oldSection.getStringList("harvest-actions.messages")); - newSection.set("points." + (max_stage-1) + ".events.break.action_message.chance", oldSection.getDouble("harvest-actions.messages-chance", 1)); - } - - if (oldSection.contains("harvest-actions.commands")) { - newSection.set("points." + (max_stage-1) + ".events.break.action_command.type", "command"); - newSection.set("points." + (max_stage-1) + ".events.break.action_command.value", oldSection.getStringList("harvest-actions.commands")); - newSection.set("points." + (max_stage-1) + ".events.break.action_command.chance", oldSection.getDouble("harvest-actions.commands-chance", 1)); - } - - if (oldSection.contains("harvest-actions.xp")) { - newSection.set("points." + (max_stage-1) + ".events.break.action_exp.type", "exp"); - newSection.set("points." + (max_stage-1) + ".events.break.action_exp.value", oldSection.getInt("harvest-actions.xp")); - newSection.set("points." + (max_stage-1) + ".events.break.action_exp.chance", oldSection.getDouble("harvest-actions.xp-chance", 1)); - } - - if (oldSection.contains("harvest-actions.skill-xp")) { - newSection.set("points." + (max_stage-1) + ".events.break.action_skill_xp.type", "skill-xp"); - newSection.set("points." + (max_stage-1) + ".events.break.action_skill_xp.value", oldSection.getInt("harvest-actions.skill-xp")); - newSection.set("points." + (max_stage-1) + ".events.break.action_skill_xp.chance", oldSection.getDouble("harvest-actions.skill-xp-chance", 1)); - } - - if (oldSection.contains("harvest-actions.job-xp")) { - newSection.set("points." + (max_stage-1) + ".events.break.action_job_xp.type", "job-xp"); - newSection.set("points." + (max_stage-1) + ".events.break.action_job_xp.value", oldSection.getInt("harvest-actions.job-xp")); - newSection.set("points." + (max_stage-1) + ".events.break.action_job_xp.chance", oldSection.getDouble("harvest-actions.job-xp-chance", 1)); - } - - newSection.set("grow-conditions.||.condition_1.type", "water_more_than"); - newSection.set("grow-conditions.||.condition_1.value", 0); - newSection.set("grow-conditions.||.condition_2.type", "random"); - newSection.set("grow-conditions.||.condition_2.value", 0.5); - - newSection.set("custom-bone-meal.default.item", "BONE_MEAL"); - newSection.set("custom-bone-meal.default.particle", "VILLAGER_HAPPY"); - newSection.set("custom-bone-meal.default.sound", "minecraft:item.bone_meal.use"); - newSection.set("custom-bone-meal.default.chance.2", 0.2); - newSection.set("custom-bone-meal.default.chance.1", 0.6); - - File output = new File(CustomCrops.getInstance().getDataFolder(), "converted" + File.separator + crop + ".yml"); - if (!output.getParentFile().exists()) output.getParentFile().mkdirs(); - try { - newConfig.save(output); - } catch (IOException ignored) { - } - } - - AdventureUtils.sendMessage(sender, "Converted! The result might not be 100% accurate."); - AdventureUtils.sendMessage(sender, "Files are stored into /CustomCrops/converted/ folder"); - AdventureUtils.sendMessage(sender, "Please double check before put into production environment"); - - return true; - } - - @Override - public List onTabComplete(CommandSender sender, List args) { - if (args.size() == 1) { - return List.of("tripwire", "item_frame"); - } - return null; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/FixCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/FixCommand.java deleted file mode 100644 index 939b763..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/FixCommand.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.command.CommandSender; - -import java.util.List; - -public class FixCommand extends AbstractSubCommand { - - public static final FixCommand INSTANCE = new FixCommand(); - - public FixCommand() { - super("fixcorrupted"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - if (lackArgs(sender, 1, args.size())) return true; - - String world = args.get(0); - CustomCrops.getInstance().getWorldDataManager().fixCorruptedData(world); - AdventureUtils.sendMessage(sender, MessageManager.prefix + "Fixing would be ongoing in next 30s"); - return true; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ForceCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ForceCommand.java deleted file mode 100644 index cc57e63..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ForceCommand.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd; - -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.command.subcmd.force.ConsumeTaskCommand; -import net.momirealms.customcrops.command.subcmd.force.CropGrowCommand; -import net.momirealms.customcrops.command.subcmd.force.SprinklerWorkCommand; - -public class ForceCommand extends AbstractSubCommand { - - public static final ForceCommand INSTANCE = new ForceCommand(); - - public ForceCommand() { - super("force"); - regSubCommand(SprinklerWorkCommand.INSTANCE); - regSubCommand(ConsumeTaskCommand.INSTANCE); - regSubCommand(CropGrowCommand.INSTANCE); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/HelpCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/HelpCommand.java deleted file mode 100644 index c0959b9..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/HelpCommand.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd; - -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.command.CommandSender; - -import java.util.List; - -public class HelpCommand extends AbstractSubCommand { - - public static final HelpCommand INSTANCE = new HelpCommand(); - - public HelpCommand() { - super("help"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - AdventureUtils.sendMessage(sender, "<#FFA500>Command usage:"); - AdventureUtils.sendMessage(sender, " ├─<#FFFACD> "); - AdventureUtils.sendMessage(sender, " └─<#FFFACD><#E1FFFF>[Optional Augument]"); - AdventureUtils.sendMessage(sender, "<#FFA500>/customcrops"); - AdventureUtils.sendMessage(sender, " ├─help"); - AdventureUtils.sendMessage(sender, " ├─about"); - AdventureUtils.sendMessage(sender, " ├─reload <#87CEFA>Reload the plugin"); - AdventureUtils.sendMessage(sender, " ├─migrate <#87CEFA>Migrate data from 2.0->3.0"); - AdventureUtils.sendMessage(sender, " ├─convert <#87CEFA>Convert config from 2.0->3.0"); - AdventureUtils.sendMessage(sender, " ├─setseason <#FFFACD> <#87CEFA>Set a world's season"); - AdventureUtils.sendMessage(sender, " ├─setdate <#FFFACD> <#87CEFA>Set a world's date"); - AdventureUtils.sendMessage(sender, " └─force"); - AdventureUtils.sendMessage(sender, " ├─sprinklerwork <#FFFACD> <#87CEFA>Forced sprinklers to work"); - AdventureUtils.sendMessage(sender, " └─consume <#FFFACD> <#87CEFA>Forced pots to reduce water amount and the remaining use of fertilizers"); - return true; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/MigrateCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/MigrateCommand.java deleted file mode 100644 index cc10877..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/MigrateCommand.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd; - -import com.google.gson.*; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.object.migrate.MigrateWorld; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.api.object.season.SeasonData; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -public class MigrateCommand extends AbstractSubCommand { - - public static final MigrateCommand INSTANCE = new MigrateCommand(); - - private final HashSet confirm; - - public MigrateCommand() { - super("migrate"); - confirm = new HashSet<>(); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - - if (!confirm.contains(sender.getName())) { - confirm.add(sender.getName()); - AdventureUtils.sendMessage(sender, "[CustomCrops] Type the command again to confirm."); - return true; - } - - confirm.remove(sender.getName()); - if (sender instanceof Player player) { - AdventureUtils.playerMessage(player, "Migration started. See the console for more information."); - } - AdventureUtils.consoleMessage("[CustomCrops] Migration has started."); - Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.getInstance(), new MigrationTask()); - return true; - } - - public static class MigrationTask implements Runnable { - - @Override - public void run() { - - File outer_folder; - if (ConfigManager.worldFolderPath.equals("")) { - outer_folder = CustomCrops.getInstance().getDataFolder().getAbsoluteFile().getParentFile().getParentFile(); - } else { - outer_folder = new File(ConfigManager.worldFolderPath); - } - if (!outer_folder.isDirectory()) { - AdventureUtils.consoleMessage("[CustomCrops] World folder is not detected"); - return; - } - - File[] files = outer_folder.listFiles(); - if (files == null) return; - - List world_folders = Arrays.stream(files).filter(File::isDirectory).toList(); - for (File world_folder : world_folders) { - File ccDataFolder = new File(world_folder, "customcrops_data"); - if (!ccDataFolder.isDirectory()) continue; - String worldName = world_folder.getName(); - AdventureUtils.consoleMessage("[CustomCrops] Migrating world: " + worldName); - MigrateWorld migrateWorld = new MigrateWorld(worldName); - migrateWorld.init(); - - try { - JsonElement json = JsonParser.parseReader(new FileReader(new File(ccDataFolder, "season.json"))); - if (json.isJsonObject()) { - JsonObject jsonObject = json.getAsJsonObject(); - if (jsonObject.has("season")) { - JsonPrimitive jsonPrimitive = jsonObject.getAsJsonPrimitive("season"); - String season = jsonPrimitive.getAsString(); - CustomCrops.getInstance().getSeasonManager().loadSeasonData(new SeasonData(worldName, CCSeason.valueOf(season), 1)); - } - } - } catch (FileNotFoundException ignored) { - } - - AdventureUtils.consoleMessage("[CustomCrops] Migrated " + worldName + "'s season"); - - try { - JsonElement json= JsonParser.parseReader(new FileReader(new File(ccDataFolder, "pot.json"))); - if (json.isJsonObject()) { - JsonObject jsonObject = json.getAsJsonObject(); - if (jsonObject.has("pot")) { - JsonArray jsonArray = jsonObject.getAsJsonArray("pot"); - for (JsonElement jsonElement : jsonArray) { - String loc = jsonElement.getAsString(); - String[] locs = loc.split(","); - SimpleLocation simpleLocation = new SimpleLocation(worldName, Integer.parseInt(locs[0]), Integer.parseInt(locs[1]), Integer.parseInt(locs[2])); - migrateWorld.addWaterToPot(simpleLocation, 1, "default"); - } - } - } - } catch (FileNotFoundException ignored) { - } - - AdventureUtils.consoleMessage("[CustomCrops] Migrated " + worldName + "'s pots"); - - YamlConfiguration data = YamlConfiguration.loadConfiguration(new File(ccDataFolder, "fertilizers.yml")); - for (String key : data.getKeys(false)) { - String[] loc = key.split(","); - SimpleLocation location = new SimpleLocation(worldName, Integer.parseInt(loc[0]), Integer.parseInt(loc[1]), Integer.parseInt(loc[2])); - String fertilizer = data.getString(key + ".type"); - int times = data.getInt(key + ".times"); - FertilizerConfig fertilizerConfig = CustomCrops.getInstance().getFertilizerManager().getConfigByKey(fertilizer); - if (fertilizerConfig != null) { - Fertilizer fertilizer1 = new Fertilizer(fertilizerConfig); - fertilizer1.setTimes(times); - migrateWorld.addFertilizerToPot(location, fertilizer1, "default"); - } - } - - AdventureUtils.consoleMessage("[CustomCrops] Migrated " + worldName + "'s fertilizers"); - - try { - JsonElement json= JsonParser.parseReader(new FileReader(new File(ccDataFolder, "scarecrow.json"))); - if (json.isJsonObject()) { - JsonObject jsonObject = json.getAsJsonObject(); - for (Map.Entry en : jsonObject.entrySet()) { - JsonArray jsonArray = en.getValue().getAsJsonArray(); - int size = jsonArray.size(); - for (int i = 0; i < size; i++) { - String[] loc = jsonArray.get(i).getAsString().split(","); - migrateWorld.addScarecrow(new SimpleLocation(worldName, Integer.parseInt(loc[0]), Integer.parseInt(loc[1]), Integer.parseInt(loc[2]))); - } - } - } - } - catch (FileNotFoundException ignore) { - } - - AdventureUtils.consoleMessage("[CustomCrops] Migrated " + worldName + "'s scarecrows"); - - YamlConfiguration cropData = YamlConfiguration.loadConfiguration(new File(ccDataFolder, "crops.yml")); - for (Map.Entry entry : cropData.getValues(false).entrySet()) { - String crop = (String) entry.getValue(); - GrowingCrop growingCrop; - if (crop.contains("_")) { - String stageStr = crop.substring(crop.indexOf("_stage_") + 7); - int stage = Integer.parseInt(stageStr); - growingCrop = new GrowingCrop(crop.substring(0, crop.indexOf("_stage_")), stage); - String[] loc = entry.getKey().split(","); - SimpleLocation simpleLocation = new SimpleLocation(worldName, Integer.parseInt(loc[0]), Integer.parseInt(loc[1]), Integer.parseInt(loc[2])); - migrateWorld.addCropData(simpleLocation, growingCrop); - } - } - - AdventureUtils.consoleMessage("[CustomCrops] Migrated " + worldName + "'s crops"); - migrateWorld.disable(); - } - - AdventureUtils.consoleMessage("[CustomCrops] Migration finished!"); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/PerformanceTest.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/PerformanceTest.java deleted file mode 100644 index bca7192..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/PerformanceTest.java +++ /dev/null @@ -1,41 +0,0 @@ -//package net.momirealms.customcrops.command.subcmd; -// -//import net.momirealms.customcrops.CustomCrops; -//import net.momirealms.customcrops.api.object.crop.GrowingCrop; -//import net.momirealms.customcrops.api.object.world.SimpleLocation; -//import net.momirealms.customcrops.api.object.world.WorldDataManager; -//import net.momirealms.customcrops.command.AbstractSubCommand; -//import org.bukkit.command.CommandSender; -//import org.bukkit.entity.Player; -// -//import java.util.List; -// -//public class PerformanceTest extends AbstractSubCommand { -// -// public static final PerformanceTest INSTANCE = new PerformanceTest(); -// -// public PerformanceTest() { -// super("test"); -// } -// -// @Override -// public boolean onCommand(CommandSender sender, List args) { -// int radius = Integer.parseInt(args.get(0)); -// WorldDataManager worldDataManager = CustomCrops.getInstance().getWorldDataManager(); -// if (sender instanceof Player player) { -// SimpleLocation simpleLocation = SimpleLocation.getByBukkitLocation(player.getLocation()); -// CustomCrops.getInstance().getScheduler().runTaskAsync(() -> { -// SimpleLocation simpleLocation1 = simpleLocation.add(-radius, 0, -radius); -// for (int i = 0; i < radius * 2; i++) { -// for (int j = 0; j < radius * 2; j++) { -// for (int y = 0; y < 10; y++) { -// SimpleLocation temp = simpleLocation1.add(i, y, j); -// worldDataManager.addCropData(temp, new GrowingCrop("tomato", 0), false); -// } -// } -// } -// }); -// } -// return true; -// } -//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ReloadCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ReloadCommand.java deleted file mode 100644 index d3f1830..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/ReloadCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.command.CommandSender; - -import java.util.List; - -public final class ReloadCommand extends AbstractSubCommand { - - public static final ReloadCommand INSTANCE = new ReloadCommand(); - - private ReloadCommand() { - super("reload"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - long time1 = System.currentTimeMillis(); - CustomCrops.getInstance().reload(); - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.reload.replace("{time}", String.valueOf(System.currentTimeMillis() - time1))); - return true; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/SetDateCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/SetDateCommand.java deleted file mode 100644 index 4e1e844..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/SetDateCommand.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.api.object.season.SeasonData; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.generator.WorldInfo; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -public class SetDateCommand extends AbstractSubCommand { - - public static final SetDateCommand INSTANCE = new SetDateCommand(); - - public SetDateCommand() { - super("setdate"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - if (lackArgs(sender, 2, args.size())) return true; - World world = Bukkit.getWorld(args.get(0)); - if (world == null) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.worldNotExist.replace("{world}", args.get(0))); - return true; - } - try { - int date = Integer.parseInt(args.get(1)); - SeasonData seasonData = CustomCrops.getInstance().getSeasonManager().unloadSeasonData(args.get(0)); - if (seasonData == null) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.noSeason); - return true; - } - seasonData.setDate(date); - CustomCrops.getInstance().getSeasonManager().loadSeasonData(seasonData); - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.setDate.replace("{world}", args.get(0)).replace("{date}", String.valueOf(date))); - return true; - } catch (NumberFormatException e) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + "Wrong number format"); - return true; - } - } - - @Override - public List onTabComplete(CommandSender sender, List args) { - if (args.size() == 1) { - return super.filterStartingWith(Bukkit.getWorlds().stream().filter(world -> CustomCrops.getInstance().getWorldDataManager().isWorldAllowed(world)).map(WorldInfo::getName).collect(Collectors.toList()), args.get(0)); - } else if (args.size() == 2) { - ArrayList dates = new ArrayList<>(); - for (int i = 1; i <= ConfigManager.seasonInterval; i++) { - dates.add(String.valueOf(i)); - } - return super.filterStartingWith(dates, args.get(1)); - } - return null; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/SetSeasonCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/SetSeasonCommand.java deleted file mode 100644 index 2aab230..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/SetSeasonCommand.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.api.object.season.SeasonData; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.generator.WorldInfo; - -import java.util.List; -import java.util.Locale; -import java.util.stream.Collectors; - -public class SetSeasonCommand extends AbstractSubCommand { - - public static final SetSeasonCommand INSTANCE = new SetSeasonCommand(); - - public SetSeasonCommand() { - super("setseason"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - if (lackArgs(sender, 2, args.size())) return true; - World world = Bukkit.getWorld(args.get(0)); - if (world == null) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.worldNotExist.replace("{world}", args.get(0))); - return true; - } - try { - CCSeason ccSeason = CCSeason.valueOf(args.get(1).toUpperCase(Locale.ENGLISH)); - SeasonData seasonData = CustomCrops.getInstance().getSeasonManager().unloadSeasonData(args.get(0)); - if (seasonData == null) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.noSeason); - return true; - } - seasonData.changeSeason(ccSeason); - CustomCrops.getInstance().getSeasonManager().loadSeasonData(seasonData); - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.setSeason.replace("{world}", args.get(0)).replace("{season}", ccSeason.getDisplay())); - return true; - } catch (IllegalArgumentException e) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.seasonNotExist.replace("{season}", args.get(1))); - return true; - } - } - - @Override - public List onTabComplete(CommandSender sender, List args) { - if (args.size() == 1) { - return super.filterStartingWith(Bukkit.getWorlds().stream().filter(world -> CustomCrops.getInstance().getWorldDataManager().isWorldAllowed(world)).map(WorldInfo::getName).collect(Collectors.toList()), args.get(0)); - } else if (args.size() == 2) { - return super.filterStartingWith(List.of("spring", "summer", "autumn", "winter"), args.get(1)); - } - return null; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/Test.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/Test.java deleted file mode 100644 index e48cbc7..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/Test.java +++ /dev/null @@ -1,122 +0,0 @@ -//package net.momirealms.customcrops.command.subcmd; -// -//import dev.lone.itemsadder.api.CustomBlock; -//import net.momirealms.customcrops.CustomCrops; -//import net.momirealms.customcrops.command.AbstractSubCommand; -//import org.bukkit.Bukkit; -//import org.bukkit.Location; -//import org.bukkit.Material; -//import org.bukkit.block.Block; -//import org.bukkit.block.BlockFace; -//import org.bukkit.command.CommandSender; -//import org.bukkit.entity.Player; -//import org.bukkit.event.EventHandler; -//import org.bukkit.event.Listener; -//import org.bukkit.event.block.BlockBreakEvent; -// -//import java.util.*; -// -//public class Test extends AbstractSubCommand implements Listener { -// -// public static final Test INSTANCE = new Test(); -// -// public Test() { -// super("test"); -// Bukkit.getPluginManager().registerEvents(this, CustomCrops.getInstance()); -// } -// -// @EventHandler -// public void onBreak(BlockBreakEvent event) { -// long time1 = System.currentTimeMillis(); -// getNearbyWires(event.getBlock()); -// long time2 = System.currentTimeMillis(); -// System.out.println("method1:" + (time2 - time1) + "ms"); -// -// getNearbyWires(event.getBlock(), new HashSet<>()); -// System.out.println("method2:" + (time2 - time1) + "ms"); -// -//// getNearbyWires(event.getBlock(), new HashSet<>()); -//// System.out.println("method2:" + (time2 - time1) + "ms"); -// } -// -// @Override -// public boolean onCommand(CommandSender sender, List args) { -// if (sender instanceof Player player) { -// Location location = player.getLocation(); -// int size = Integer.parseInt(args.get(0)) / 2; -// for (int i = -size; i < size; i++) { -// for (int j = -size; j < size; j++) { -// CustomBlock.place("customcrops:tomato_stage_1", location.clone().add(i, 0, j)); -// } -// } -// } -// return true; -// } -// -// public static Collection getNearbyWires(Block startBlock) { -// Set blocks = new HashSet<>(); -// Queue queue = new LinkedList<>(); -// queue.add(startBlock); -// -// while (!queue.isEmpty()) { -// Block currentBlock = queue.remove(); -// for (BlockFace face : XZ_FACES) { -// Block b = currentBlock.getRelative(face); -// if (b.getType() == Material.TRIPWIRE && !blocks.contains(b)) { -// blocks.add(b); -// queue.add(b); -// } -// } -// } -// -// return blocks; -// } -// -// public static Collection getNearbyWires2(Block startBlock) { -// Set blocks = new HashSet<>(); -// Queue queue = new LinkedList<>(); -// queue.add(startBlock); -// -// while (!queue.isEmpty()) { -// Block currentBlock = queue.remove(); -// for (int x = -1; x <= 1; x++) { -// for (int z = -1; z <= 1; z++) { -// if ((x == 0 && z == 0) || (Math.abs(x) == 1 && Math.abs(z) == 1)) -// continue; -// Block b = currentBlock.getLocation().clone().add(x, 0, z).getBlock(); -// if (!blocks.contains(b) && b.getType() == Material.TRIPWIRE) { -// blocks.add(b); -// queue.add(b); -// } -// } -// } -// } -// -// return blocks; -// } -// -// private static final BlockFace[] XZ_FACES = { -// BlockFace.NORTH, -// BlockFace.EAST, -// BlockFace.SOUTH, -// BlockFace.WEST -// }; -// -// public static Collection getNearbyWires(Block startBlock, Set blocks) -// { -// // Avoid too much processing, i don't give a shit about far blocks. -// if (blocks.size() > 256) // 256 is ~1 chunk -// return blocks; -// -// for (BlockFace face : XZ_FACES) -// { -// Block currentBlock = startBlock.getRelative(face); -// if (currentBlock.getType() == Material.TRIPWIRE && !blocks.contains(currentBlock)) -// { -// blocks.add(currentBlock); -// blocks.addAll(getNearbyWires(currentBlock, blocks)); -// } -// } -// return blocks; -// } -//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/ConsumeTaskCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/ConsumeTaskCommand.java deleted file mode 100644 index c6bdb55..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/ConsumeTaskCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd.force; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.api.object.world.CCWorld; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.generator.WorldInfo; - -import java.util.List; -import java.util.stream.Collectors; - -public class ConsumeTaskCommand extends AbstractSubCommand { - - public static final ConsumeTaskCommand INSTANCE = new ConsumeTaskCommand(); - - public ConsumeTaskCommand() { - super("consume"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - if (lackArgs(sender, 2, args.size())) return true; - World world = Bukkit.getWorld(args.get(0)); - if (world == null) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.worldNotExist.replace("{world}", args.get(0))); - return true; - } - int seconds = Integer.parseInt(args.get(1)); - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.forceConsume.replace("{world}", args.get(0))); - CustomCrops.getInstance().getScheduler().runTaskAsync(() -> { - CCWorld ccworld = CustomCrops.getInstance().getWorldDataManager().getWorld(args.get(0)); - if (ccworld != null) { - ccworld.scheduleConsumeTask(seconds); - } - }); - return true; - } - - @Override - public List onTabComplete(CommandSender sender, List args) { - if (args.size() == 1) { - return super.filterStartingWith(Bukkit.getWorlds().stream().filter(world -> CustomCrops.getInstance().getWorldDataManager().isWorldAllowed(world)).map(WorldInfo::getName).collect(Collectors.toList()), args.get(0)); - } - if (args.size() == 2) { - return List.of(""); - } - return null; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/CropGrowCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/CropGrowCommand.java deleted file mode 100644 index 1dfa240..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/CropGrowCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd.force; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.api.object.world.CCWorld; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.generator.WorldInfo; - -import java.util.List; -import java.util.stream.Collectors; - -public class CropGrowCommand extends AbstractSubCommand { - - public static final CropGrowCommand INSTANCE = new CropGrowCommand(); - - public CropGrowCommand() { - super("cropgrow"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - if (lackArgs(sender, 2, args.size())) return true; - World world = Bukkit.getWorld(args.get(0)); - if (world == null) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.worldNotExist.replace("{world}", args.get(0))); - return true; - } - int seconds = Integer.parseInt(args.get(1)); - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.forceGrow.replace("{world}", args.get(0))); - CustomCrops.getInstance().getScheduler().runTaskAsync(() -> { - CCWorld ccworld = CustomCrops.getInstance().getWorldDataManager().getWorld(args.get(0)); - if (ccworld != null) { - ccworld.scheduleCropGrowTask(seconds); - } - }); - return true; - } - - @Override - public List onTabComplete(CommandSender sender, List args) { - if (args.size() == 1) { - return super.filterStartingWith(Bukkit.getWorlds().stream().filter(world -> CustomCrops.getInstance().getWorldDataManager().isWorldAllowed(world)).map(WorldInfo::getName).collect(Collectors.toList()), args.get(0)); - } - if (args.size() == 2) { - return List.of(""); - } - return null; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/SprinklerWorkCommand.java b/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/SprinklerWorkCommand.java deleted file mode 100644 index 7461f25..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/command/subcmd/force/SprinklerWorkCommand.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.command.subcmd.force; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.api.object.world.CCWorld; -import net.momirealms.customcrops.command.AbstractSubCommand; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.generator.WorldInfo; - -import java.util.List; -import java.util.stream.Collectors; - -public class SprinklerWorkCommand extends AbstractSubCommand { - - public static final SprinklerWorkCommand INSTANCE = new SprinklerWorkCommand(); - - public SprinklerWorkCommand() { - super("sprinklerwork"); - } - - @Override - public boolean onCommand(CommandSender sender, List args) { - if (lackArgs(sender, 2, args.size())) return true; - World world = Bukkit.getWorld(args.get(0)); - if (world == null) { - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.worldNotExist.replace("{world}", args.get(0))); - return true; - } - int seconds = Integer.parseInt(args.get(1)); - AdventureUtils.sendMessage(sender, MessageManager.prefix + MessageManager.forceWork.replace("{world}", args.get(0))); - CustomCrops.getInstance().getScheduler().runTaskAsync(() -> { - CCWorld ccworld = CustomCrops.getInstance().getWorldDataManager().getWorld(args.get(0)); - if (ccworld != null) { - ccworld.scheduleSprinklerWork(seconds); - } - }); - return true; - } - - @Override - public List onTabComplete(CommandSender sender, List args) { - if (args.size() == 1) { - return super.filterStartingWith(Bukkit.getWorlds().stream().filter(world -> CustomCrops.getInstance().getWorldDataManager().isWorldAllowed(world)).map(WorldInfo::getName).collect(Collectors.toList()), args.get(0)); - } - if (args.size() == 2) { - return List.of(""); - } - return null; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java new file mode 100644 index 0000000..0997811 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/IntegrationManagerImpl.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.compatibility; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.SeasonInterface; +import net.momirealms.customcrops.api.manager.IntegrationManager; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.compatibility.item.MMOItemsItemImpl; +import net.momirealms.customcrops.compatibility.item.MythicMobsItemImpl; +import net.momirealms.customcrops.compatibility.item.NeigeItemsItemImpl; +import net.momirealms.customcrops.compatibility.item.ZaphkielItemImpl; +import net.momirealms.customcrops.compatibility.level.*; +import net.momirealms.customcrops.compatibility.season.AdvancedSeasonsImpl; +import net.momirealms.customcrops.compatibility.season.InBuiltSeason; +import net.momirealms.customcrops.compatibility.season.RealisticSeasonsImpl; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; + +public class IntegrationManagerImpl implements IntegrationManager { + + private final CustomCropsPlugin plugin; + private final HashMap levelPluginMap; + private SeasonInterface seasonInterface; + + public IntegrationManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.levelPluginMap = new HashMap<>(); + } + + @Override + public void init() { + if (plugin.isHookedPluginEnabled("MMOItems")) { + plugin.getItemManager().registerItemLibrary(new MMOItemsItemImpl()); + hookMessage("MMOItems"); + } + if (plugin.isHookedPluginEnabled("Zaphkiel")) { + plugin.getItemManager().registerItemLibrary(new ZaphkielItemImpl()); + hookMessage("Zaphkiel"); + } + if (plugin.isHookedPluginEnabled("NeigeItems")) { + plugin.getItemManager().registerItemLibrary(new NeigeItemsItemImpl()); + hookMessage("NeigeItems"); + } + if (plugin.isHookedPluginEnabled("MythicMobs")) { + plugin.getItemManager().registerItemLibrary(new MythicMobsItemImpl()); + hookMessage("MythicMobs"); + } + if (plugin.isHookedPluginEnabled("EcoJobs")) { + registerLevelPlugin("EcoJobs", new EcoJobsImpl()); + hookMessage("EcoJobs"); + } + if (plugin.isHookedPluginEnabled("AureliumSkills")) { + registerLevelPlugin("AureliumSkills", new AureliumSkillsImpl()); + hookMessage("AureliumSkills"); + } + if (plugin.isHookedPluginEnabled("EcoSkills")) { + registerLevelPlugin("EcoSkills", new EcoSkillsImpl()); + hookMessage("EcoSkills"); + } + if (plugin.isHookedPluginEnabled("mcMMO")) { + registerLevelPlugin("mcMMO", new McMMOImpl()); + hookMessage("mcMMO"); + } + if (plugin.isHookedPluginEnabled("MMOCore")) { + registerLevelPlugin("MMOCore", new MMOCoreImpl()); + hookMessage("MMOCore"); + } + if (plugin.isHookedPluginEnabled("AuraSkills")) { + registerLevelPlugin("AuraSkills", new AuraSkillsImpl()); + hookMessage("AuraSkills"); + } + if (plugin.isHookedPluginEnabled("RealisticSeasons")) { + this.seasonInterface = new RealisticSeasonsImpl(); + hookMessage("RealisticSeasons"); + } else if (plugin.isHookedPluginEnabled("AdvancedSeasons")) { + this.seasonInterface = new AdvancedSeasonsImpl(); + hookMessage("AdvancedSeasons"); + } else { + this.seasonInterface = new InBuiltSeason(plugin.getWorldManager()); + } + } + + @Override + public void disable() { + + } + + /** + * Registers a level plugin with the specified name. + * + * @param plugin The name of the level plugin. + * @param level The implementation of the LevelInterface. + * @return true if the registration was successful, false if the plugin name is already registered. + */ + @Override + public boolean registerLevelPlugin(String plugin, LevelInterface level) { + if (levelPluginMap.containsKey(plugin)) return false; + levelPluginMap.put(plugin, level); + return true; + } + + /** + * Unregisters a level plugin with the specified name. + * + * @param plugin The name of the level plugin to unregister. + * @return true if the unregistration was successful, false if the plugin name is not found. + */ + @Override + public boolean unregisterLevelPlugin(String plugin) { + return levelPluginMap.remove(plugin) != null; + } + + private void hookMessage(String plugin) { + LogUtils.info( plugin + " hooked!"); + } + + /** + * Get the LevelInterface provided by a plugin. + * + * @param plugin The name of the plugin providing the LevelInterface. + * @return The LevelInterface provided by the specified plugin, or null if the plugin is not registered. + */ + @Override + @Nullable + public LevelInterface getLevelPlugin(String plugin) { + return levelPluginMap.get(plugin); + } + + @Override + public SeasonInterface getSeasonInterface() { + return seasonInterface; + } + + @Override + public Season getSeason(World world) { + return seasonInterface.getSeason(world); + } + + @Override + public int getDate(World world) { + return seasonInterface.getDate(world); + } + + @Override + public ItemStack build(String itemID) { + return null; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/VaultHook.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/VaultHook.java similarity index 69% rename from plugin/src/main/java/net/momirealms/customcrops/integration/VaultHook.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/VaultHook.java index cd880b0..2da91f4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/VaultHook.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/VaultHook.java @@ -15,26 +15,26 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration; +package net.momirealms.customcrops.compatibility; import net.milkbowl.vault.economy.Economy; -import net.momirealms.customcrops.CustomCrops; +import net.momirealms.customcrops.api.CustomCropsPlugin; import org.bukkit.plugin.RegisteredServiceProvider; public class VaultHook { - private Economy economy; + private static Economy economy; - public boolean initialize() { - RegisteredServiceProvider rsp = CustomCrops.getInstance().getServer().getServicesManager().getRegistration(Economy.class); + public static boolean initialize() { + RegisteredServiceProvider rsp = CustomCropsPlugin.getInstance().getServer().getServicesManager().getRegistration(Economy.class); if (rsp == null) { return false; } - this.economy = rsp.getProvider(); + economy = rsp.getProvider(); return true; } - public Economy getEconomy() { + public static Economy getEconomy() { return economy; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/item/MMOItemsItemImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java similarity index 58% rename from plugin/src/main/java/net/momirealms/customcrops/integration/item/MMOItemsItemImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java index cae441b..6d8ad25 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/item/MMOItemsItemImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MMOItemsItemImpl.java @@ -15,27 +15,37 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.item; +package net.momirealms.customcrops.compatibility.item; +import de.tr7zw.changeme.nbtapi.NBTItem; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem; -import net.momirealms.customcrops.integration.ItemInterface; +import net.momirealms.customcrops.api.integration.ItemLibrary; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; import java.util.Locale; -public class MMOItemsItemImpl implements ItemInterface { +public class MMOItemsItemImpl implements ItemLibrary { - @Nullable @Override - public ItemStack build(String material, Player player) { - if (!material.startsWith("MMOItems:")) return null; - material = material.substring(9); - String[] split = material.split(":", 2); + public String identification() { + return "MMOItems"; + } + + @Override + public ItemStack buildItem(Player player, String id) { + String[] split = id.split(":"); MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase(Locale.ENGLISH)); - return mmoItem == null ? null : mmoItem.newBuilder().build(); + return mmoItem == null ? new ItemStack(Material.AIR) : mmoItem.newBuilder().build(); + } + + @Override + public String getItemID(ItemStack itemStack) { + NBTItem nbtItem = new NBTItem(itemStack); + if (!nbtItem.hasTag("MMOITEMS_ITEM_ID")) return null; + return nbtItem.getString("MMOITEMS_ITEM_ID"); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/item/MythicMobsItemImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java similarity index 63% rename from plugin/src/main/java/net/momirealms/customcrops/integration/item/MythicMobsItemImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java index 6308dac..392ee4c 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/item/MythicMobsItemImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/MythicMobsItemImpl.java @@ -15,15 +15,15 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.item; +package net.momirealms.customcrops.compatibility.item; +import de.tr7zw.changeme.nbtapi.NBTItem; import io.lumine.mythic.bukkit.MythicBukkit; -import net.momirealms.customcrops.integration.ItemInterface; +import net.momirealms.customcrops.api.integration.ItemLibrary; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; -public class MythicMobsItemImpl implements ItemInterface { +public class MythicMobsItemImpl implements ItemLibrary { private MythicBukkit mythicBukkit; @@ -32,13 +32,21 @@ public class MythicMobsItemImpl implements ItemInterface { } @Override - @Nullable - public ItemStack build(String material, Player player) { - if (!material.startsWith("MythicMobs:")) return null; - material = material.substring(11); + public String identification() { + return "MythicMobs"; + } + + @Override + public ItemStack buildItem(Player player, String id) { if (mythicBukkit == null || mythicBukkit.isClosed()) { this.mythicBukkit = MythicBukkit.inst(); } - return mythicBukkit.getItemManager().getItemStack(material); + return mythicBukkit.getItemManager().getItemStack(id); } -} + + @Override + public String getItemID(ItemStack itemStack) { + NBTItem nbtItem = new NBTItem(itemStack); + return nbtItem.hasTag("MYTHIC_TYPE") ? nbtItem.getString("MYTHIC_TYPE") : null; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/NeigeItemsItemImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/NeigeItemsItemImpl.java new file mode 100644 index 0000000..53a56c0 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/NeigeItemsItemImpl.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.compatibility.item; + +import net.momirealms.customcrops.api.integration.ItemLibrary; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import pers.neige.neigeitems.item.ItemInfo; +import pers.neige.neigeitems.manager.ItemManager; +import pers.neige.neigeitems.utils.ItemUtils; + +public class NeigeItemsItemImpl implements ItemLibrary { + + @Override + public String identification() { + return "NeigeItems"; + } + + @Override + public ItemStack buildItem(Player player, String id) { + return ItemManager.INSTANCE.getItemStack(id, player); + } + + @Override + public String getItemID(ItemStack itemStack) { + ItemInfo itemInfo = ItemUtils.isNiItem(itemStack); + if (itemInfo != null) { + return itemInfo.getId(); + } + return null; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/ZaphkielItemImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/ZaphkielItemImpl.java new file mode 100644 index 0000000..8b79a93 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/item/ZaphkielItemImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.compatibility.item; + +import ink.ptms.zaphkiel.ZapAPI; +import ink.ptms.zaphkiel.Zaphkiel; +import net.momirealms.customcrops.api.integration.ItemLibrary; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class ZaphkielItemImpl implements ItemLibrary { + + private final ZapAPI zapAPI; + + public ZaphkielItemImpl() { + this.zapAPI = Zaphkiel.INSTANCE.api(); + } + + @Override + public String identification() { + return "Zaphkiel"; + } + + @Override + public ItemStack buildItem(Player player, String id) { + return zapAPI.getItemManager().generateItemStack(id, player); + } + + @Override + public String getItemID(ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR) return null; + return zapAPI.getItemHandler().getItemId(itemStack); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AuraSkillsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AuraSkillsImpl.java new file mode 100644 index 0000000..8c80239 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AuraSkillsImpl.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.compatibility.level; + +import dev.aurelium.auraskills.api.AuraSkillsApi; +import dev.aurelium.auraskills.api.registry.NamespacedId; +import net.momirealms.customcrops.api.integration.LevelInterface; +import org.bukkit.entity.Player; + +public class AuraSkillsImpl implements LevelInterface { + + @Override + public void addXp(Player player, String target, double amount) { + AuraSkillsApi.get().getUser(player.getUniqueId()) + .addSkillXp(AuraSkillsApi.get().getGlobalRegistry().getSkill(NamespacedId.fromDefault(target)), amount); + } + + @Override + public int getLevel(Player player, String target) { + return AuraSkillsApi.get().getUser(player.getUniqueId()).getSkillLevel( + AuraSkillsApi.get().getGlobalRegistry().getSkill(NamespacedId.fromDefault(target)) + ); + } +} + diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/skill/AureliumsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java similarity index 63% rename from plugin/src/main/java/net/momirealms/customcrops/integration/skill/AureliumsImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java index ed17b43..94f94a3 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/skill/AureliumsImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/AureliumSkillsImpl.java @@ -15,31 +15,28 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.skill; +package net.momirealms.customcrops.compatibility.level; import com.archyx.aureliumskills.api.AureliumAPI; import com.archyx.aureliumskills.leveler.Leveler; -import com.archyx.aureliumskills.skills.Skill; -import net.momirealms.customcrops.integration.SkillInterface; +import net.momirealms.customcrops.api.integration.LevelInterface; import org.bukkit.entity.Player; -public class AureliumsImpl implements SkillInterface { +public class AureliumSkillsImpl implements LevelInterface { private final Leveler leveler; - private final Skill skill; - public AureliumsImpl() { + public AureliumSkillsImpl() { leveler = AureliumAPI.getPlugin().getLeveler(); - skill = AureliumAPI.getPlugin().getSkillRegistry().getSkill("farming"); } @Override - public void addXp(Player player, double amount) { - leveler.addXp(player, skill, amount); + public void addXp(Player player, String target, double amount) { + leveler.addXp(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target), amount); } @Override - public int getLevel(Player player) { - return AureliumAPI.getSkillLevel(player, skill); + public int getLevel(Player player, String target) { + return AureliumAPI.getSkillLevel(player, AureliumAPI.getPlugin().getSkillRegistry().getSkill(target)); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/job/EcoJobsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoJobsImpl.java similarity index 61% rename from plugin/src/main/java/net/momirealms/customcrops/integration/job/EcoJobsImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoJobsImpl.java index 15d3529..ea60512 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/job/EcoJobsImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoJobsImpl.java @@ -15,32 +15,29 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.job; +package net.momirealms.customcrops.compatibility.level; import com.willfp.ecojobs.api.EcoJobsAPI; import com.willfp.ecojobs.jobs.Job; -import net.momirealms.customcrops.integration.JobInterface; +import com.willfp.ecojobs.jobs.Jobs; +import net.momirealms.customcrops.api.integration.LevelInterface; import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; -public class EcoJobsImpl implements JobInterface { +public class EcoJobsImpl implements LevelInterface { @Override - public void addXp(Player player, double amount, @Nullable String jobName) { - if (jobName == null) jobName = "farmer"; + public void addXp(Player player, String target, double amount) { for (Job job : EcoJobsAPI.getActiveJobs(player)) { - if (job.getId().equals(jobName)) { + if (job.getId().equals(target)) { EcoJobsAPI.giveJobExperience(player, job, amount); - break; } } } @Override - public int getLevel(Player player, @Nullable String jobName) { -// Job job = Jobs.getByID("farmer"); -// if (job == null) return 0; -// return EcoJobsAPI.getJobLevel(player, job); - return 0; + public int getLevel(Player player, String target) { + Job job = Jobs.getByID(target); + if (job == null) return 0; + return EcoJobsAPI.getJobLevel(player, job); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/skill/EcoSkillsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoSkillsImpl.java similarity index 61% rename from plugin/src/main/java/net/momirealms/customcrops/integration/skill/EcoSkillsImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoSkillsImpl.java index 464ecd1..0ae090f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/skill/EcoSkillsImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/EcoSkillsImpl.java @@ -15,22 +15,24 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.skill; +package net.momirealms.customcrops.compatibility.level; import com.willfp.ecoskills.api.EcoSkillsAPI; import com.willfp.ecoskills.skills.Skills; -import net.momirealms.customcrops.integration.SkillInterface; +import net.momirealms.customcrops.api.integration.LevelInterface; import org.bukkit.entity.Player; -public class EcoSkillsImpl implements SkillInterface { +import java.util.Objects; + +public class EcoSkillsImpl implements LevelInterface { @Override - public void addXp(Player player, double amount) { - EcoSkillsAPI.gainSkillXP(player, Skills.INSTANCE.getByID("farming"), amount); + public void addXp(Player player, String target, double amount) { + EcoSkillsAPI.gainSkillXP(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target)), amount); } @Override - public int getLevel(Player player) { - return EcoSkillsAPI.getSkillLevel(player, Skills.INSTANCE.getByID("farming")); + public int getLevel(Player player, String target) { + return EcoSkillsAPI.getSkillLevel(player, Objects.requireNonNull(Skills.INSTANCE.getByID(target))); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/JobsRebornImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/JobsRebornImpl.java new file mode 100644 index 0000000..0b12ee1 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/JobsRebornImpl.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.compatibility.level; + +import com.gamingmesh.jobs.Jobs; +import com.gamingmesh.jobs.container.Job; +import com.gamingmesh.jobs.container.JobProgression; +import com.gamingmesh.jobs.container.JobsPlayer; +import net.momirealms.customcrops.api.integration.LevelInterface; +import org.bukkit.entity.Player; + +import java.util.List; + +public class JobsRebornImpl implements LevelInterface { + + @Override + public void addXp(Player player, String target, double amount) { + JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); + if (jobsPlayer != null) { + List jobs = jobsPlayer.getJobProgression(); + Job job = Jobs.getJob(target); + for (JobProgression progression : jobs) + if (progression.getJob().equals(job)) + progression.addExperience(amount); + } + } + + @Override + public int getLevel(Player player, String target) { + JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); + if (jobsPlayer != null) { + List jobs = jobsPlayer.getJobProgression(); + Job job = Jobs.getJob(target); + for (JobProgression progression : jobs) + if (progression.getJob().equals(job)) + return progression.getLevel(); + } + return 0; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/skill/MMOCoreImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/MMOCoreImpl.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/integration/skill/MMOCoreImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/level/MMOCoreImpl.java index ae1dcb9..ce1758d 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/skill/MMOCoreImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/MMOCoreImpl.java @@ -15,32 +15,23 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.skill; +package net.momirealms.customcrops.compatibility.level; import net.Indyuce.mmocore.MMOCore; import net.Indyuce.mmocore.api.player.PlayerData; import net.Indyuce.mmocore.experience.EXPSource; -import net.Indyuce.mmocore.experience.Profession; -import net.momirealms.customcrops.integration.SkillInterface; +import net.momirealms.customcrops.api.integration.LevelInterface; import org.bukkit.entity.Player; -public class MMOCoreImpl implements SkillInterface { +public class MMOCoreImpl implements LevelInterface { - private final Profession profession; - - public MMOCoreImpl(String name) { - profession = MMOCore.plugin.professionManager.get(name); + @Override + public void addXp(Player player, String target, double amount) { + MMOCore.plugin.professionManager.get(target).giveExperience(PlayerData.get(player), amount, null ,EXPSource.OTHER); } @Override - public void addXp(Player player, double amount) { - if (profession != null) { - profession.giveExperience(PlayerData.get(player.getUniqueId()), amount, null ,EXPSource.OTHER); - } - } - - @Override - public int getLevel(Player player) { - return PlayerData.get(player.getUniqueId()).getCollectionSkills().getLevel(profession); + public int getLevel(Player player, String target) { + return PlayerData.get(player).getCollectionSkills().getLevel(MMOCore.plugin.professionManager.get(target)); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/skill/mcMMOImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/McMMOImpl.java similarity index 65% rename from plugin/src/main/java/net/momirealms/customcrops/integration/skill/mcMMOImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/level/McMMOImpl.java index 48c2842..7bc4f74 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/skill/mcMMOImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/level/McMMOImpl.java @@ -15,22 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.skill; +package net.momirealms.customcrops.compatibility.level; import com.gmail.nossr50.api.ExperienceAPI; import com.gmail.nossr50.datatypes.skills.PrimarySkillType; -import net.momirealms.customcrops.integration.SkillInterface; +import net.momirealms.customcrops.api.integration.LevelInterface; import org.bukkit.entity.Player; -public class mcMMOImpl implements SkillInterface { +public class McMMOImpl implements LevelInterface { @Override - public void addXp(Player player, double amount) { - ExperienceAPI.addRawXP(player, "Herbalism", (float) amount, "UNKNOWN"); + public void addXp(Player player, String target, double amount) { + ExperienceAPI.addRawXP(player, target, (float) amount, "UNKNOWN"); } @Override - public int getLevel(Player player) { - return ExperienceAPI.getLevel(player, PrimarySkillType.HERBALISM); + public int getLevel(Player player, String target) { + return ExperienceAPI.getLevel(player, PrimarySkillType.valueOf(target)); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java new file mode 100644 index 0000000..caff5e2 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/CCPapi.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.compatibility.papi; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.MessageManager; +import net.momirealms.customcrops.api.util.LogUtils; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CCPapi extends PlaceholderExpansion { + + private final CustomCropsPlugin plugin; + + public CCPapi(CustomCropsPlugin plugin) { + this.plugin = plugin; + } + + public void load() { + super.register(); + } + + public void unload() { + super.unregister(); + } + + @Override + public @NotNull String getIdentifier() { + return "customcrops"; + } + + @Override + public @NotNull String getAuthor() { + return "XiaoMoMi"; + } + + @Override + public @NotNull String getVersion() { + return "3.4"; + } + + @Override + public boolean persist() { + return true; + } + + @Override + public @Nullable String onRequest(OfflinePlayer offlinePlayer, @NotNull String params) { + String[] split = params.split("_", 2); + + Player player = offlinePlayer.getPlayer(); + if (player == null) + return null; + + switch (split[0]) { + case "season" -> { + if (split.length == 1) { + return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeason(player.getWorld())); + } else { + try { + return MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeason(Bukkit.getWorld(split[1]))); + } catch (NullPointerException e) { + LogUtils.severe("World " + split[1] + " does not exist"); + e.printStackTrace(); + } + } + } + case "date" -> { + if (split.length == 1) { + return String.valueOf(plugin.getIntegrationManager().getDate(player.getWorld())); + } else { + try { + return String.valueOf(plugin.getIntegrationManager().getDate(Bukkit.getWorld(split[1]))); + } catch (NullPointerException e) { + LogUtils.severe("World " + split[1] + " does not exist"); + e.printStackTrace(); + } + } + } + } + + return null; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/papi/ParseUtil.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/ParseUtils.java similarity index 93% rename from plugin/src/main/java/net/momirealms/customcrops/integration/papi/ParseUtil.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/ParseUtils.java index fca8180..c99a86a 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/papi/ParseUtil.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/papi/ParseUtils.java @@ -15,13 +15,13 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.papi; +package net.momirealms.customcrops.compatibility.papi; import me.clip.placeholderapi.PlaceholderAPI; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; -public class ParseUtil { +public class ParseUtils { public static String setPlaceholders(Player player, String text) { return PlaceholderAPI.setPlaceholders(player, text); diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/season/AdvancedSeasonsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java similarity index 63% rename from plugin/src/main/java/net/momirealms/customcrops/integration/season/AdvancedSeasonsImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java index 570db96..86dc198 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/season/AdvancedSeasonsImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/AdvancedSeasonsImpl.java @@ -15,12 +15,12 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.season; +package net.momirealms.customcrops.compatibility.season; import net.advancedplugins.seasons.api.AdvancedSeasonsAPI; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.integration.SeasonInterface; -import org.bukkit.Bukkit; +import net.momirealms.customcrops.api.integration.SeasonInterface; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import org.bukkit.World; public class AdvancedSeasonsImpl implements SeasonInterface { @@ -31,18 +31,18 @@ public class AdvancedSeasonsImpl implements SeasonInterface { } @Override - public CCSeason getSeason(String world) { - return switch (api.getSeason(Bukkit.getWorld(world))) { - case "SPRING" -> CCSeason.SPRING; - case "WINTER" -> CCSeason.WINTER; - case "SUMMER" -> CCSeason.SUMMER; - case "FALL" -> CCSeason.AUTUMN; - default -> CCSeason.UNKNOWN; + public Season getSeason(World world) { + return switch (api.getSeason(world)) { + case "SPRING" -> Season.SPRING; + case "WINTER" -> Season.WINTER; + case "SUMMER" -> Season.SUMMER; + case "FALL" -> Season.AUTUMN; + default -> null; }; } @Override - public int getDate(String world) { + public int getDate(World world) { return 0; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/InBuiltSeason.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/InBuiltSeason.java new file mode 100644 index 0000000..91d568a --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/InBuiltSeason.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.compatibility.season; + +import net.momirealms.customcrops.api.integration.SeasonInterface; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.WorldManager; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import org.bukkit.World; +import org.jetbrains.annotations.Nullable; + +public class InBuiltSeason implements SeasonInterface { + + private final WorldManager worldManager; + + public InBuiltSeason(WorldManager worldManager) { + this.worldManager = worldManager; + } + + @Override + public @Nullable Season getSeason(World world) { + return worldManager + .getCustomCropsWorld(world) + .map(CustomCropsWorld::getSeason) + .orElse(null); + } + + @Override + public int getDate(World world) { + if (ConfigManager.syncSeasons()) + world = ConfigManager.referenceWorld(); + if (world == null) + return 0; + return worldManager + .getCustomCropsWorld(world) + .map(cropsWorld -> cropsWorld.getInfoData().getDate()) + .orElse(0); + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/season/RealisticSeasonsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/RealisticSeasonsImpl.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/integration/season/RealisticSeasonsImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/compatibility/season/RealisticSeasonsImpl.java index bbfab2a..d722f05 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/season/RealisticSeasonsImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/compatibility/season/RealisticSeasonsImpl.java @@ -15,28 +15,29 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.season; +package net.momirealms.customcrops.compatibility.season; import me.casperge.realisticseasons.api.SeasonsAPI; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.integration.SeasonInterface; -import org.bukkit.Bukkit; +import net.momirealms.customcrops.api.integration.SeasonInterface; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import org.bukkit.World; +import org.jetbrains.annotations.Nullable; public class RealisticSeasonsImpl implements SeasonInterface { @Override - public CCSeason getSeason(String world) { - return switch (SeasonsAPI.getInstance().getSeason(Bukkit.getWorld(world))) { - case WINTER -> CCSeason.WINTER; - case SPRING -> CCSeason.SPRING; - case SUMMER -> CCSeason.SUMMER; - case FALL -> CCSeason.AUTUMN; - case DISABLED, RESTORE -> CCSeason.UNKNOWN; + public @Nullable Season getSeason(World world) { + return switch (SeasonsAPI.getInstance().getSeason(world)) { + case WINTER -> Season.WINTER; + case SPRING -> Season.SPRING; + case SUMMER -> Season.SUMMER; + case FALL -> Season.AUTUMN; + case DISABLED, RESTORE -> null; }; } @Override - public int getDate(String world) { - return SeasonsAPI.getInstance().getDate(Bukkit.getWorld(world)).getDay(); + public int getDate(World world) { + return SeasonsAPI.getInstance().getDate(world).getDay(); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/Handler.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/Handler.java deleted file mode 100644 index 07339a9..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/Handler.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.customplugin; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.pot.PotManager; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.event.Cancellable; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.block.*; -import org.bukkit.event.entity.EntityChangeBlockEvent; -import org.bukkit.event.player.PlayerInteractEvent; - -import java.util.List; - -public abstract class Handler extends Function implements Listener { - - protected PlatformManager platformManager; - - public Handler(PlatformManager platformManager) { - this.platformManager = platformManager; - } - - @Override - public void load() { - Bukkit.getPluginManager().registerEvents(this, CustomCrops.getInstance()); - } - - @Override - public void unload() { - HandlerList.unregisterAll(this); - } - - @EventHandler - public void onInteract(PlayerInteractEvent event) { - platformManager.onInteractBlock(event); - } - - @EventHandler - public void onBreak(BlockBreakEvent event) { - platformManager.onBreakVanillaBlock(event); - } - - @EventHandler - public void onPlace(BlockPlaceEvent event) { - platformManager.onPlaceVanillaBlock(event); - } - - @EventHandler - public void onMoistureChange(MoistureChangeEvent event) { - if (event.isCancelled()) return; - if (ConfigManager.disableMoistureMechanic) event.setCancelled(true); - } - - @EventHandler - public void onTrampling(EntityChangeBlockEvent event) { - if (event.isCancelled()) return; - Block block = event.getBlock(); - if (block.getType() == Material.FARMLAND && event.getTo() == Material.DIRT) { - if (ConfigManager.preventTrampling) { - event.setCancelled(true); - } else if (PotManager.enableFarmLand) { - platformManager.onBreakPot(event.getEntity(), "FARMLAND", block.getLocation(), event); - } - } - } - - @EventHandler - public void onBlockFade(BlockFadeEvent event) { - if (!PotManager.enableFarmLand || event.isCancelled()) return; - Block block = event.getBlock(); - if (block.getType() == Material.FARMLAND) { - if (CustomCrops.getInstance().getPlatformInterface().detectAnyThing(event.getBlock().getLocation().clone().add(0,1,0))) { - event.setCancelled(true); - } else { - platformManager.onBreakPot(null, "FARMLAND", block.getLocation(), event); - } - } - } - - @EventHandler - public void onPistonExtend(BlockPistonExtendEvent event) { - if (!PotManager.enableVanillaBlock || event.isCancelled()) return; - handlePiston(event.getBlocks(), event); - } - - @EventHandler - public void onPistonRetract(BlockPistonRetractEvent event) { - if (!PotManager.enableVanillaBlock || event.isCancelled()) return; - handlePiston(event.getBlocks(), event); - } - - public void handlePiston(List blocks, Cancellable event) { - PotManager potManager = CustomCrops.getInstance().getPotManager(); - for (Block block : blocks) { - String id = block.getType().name(); - if (potManager.containsPotBlock(id)) { - platformManager.onBreakPot(null, id, block.getLocation(), event); - } - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/PlatformInterface.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/PlatformInterface.java deleted file mode 100644 index c9e7c7c..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/PlatformInterface.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.customplugin; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.ItemMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Interaction; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.ItemFrame; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -public interface PlatformInterface { - - /** - * This method is used for removing custom blocks - * @param location location - * @return false if it is not a custom one - */ - boolean removeCustomBlock(Location location); - - /** - * This method is used for removing any block - * @param location location - * @return false if there's no block - */ - default boolean removeAnyBlock(Location location) { - Block block = location.getBlock(); - if (block.getType() == Material.AIR) { - return false; - } - if (!removeCustomBlock(location)) { - block.setType(Material.AIR); - } - return true; - } - - default boolean removeAnyBlock(Block block) { - if (block.getType() == Material.AIR) { - return false; - } - if (!removeCustomBlock(block.getLocation())) { - block.setType(Material.AIR); - } - return true; - } - - /** - * Get custom block id at a certain location - * @param location location - * @return block id - */ - @Nullable - String getCustomBlockID(Location location); - - /** - * Get item by id - * @param id id - * @return itemStack - */ - @Nullable - ItemStack getItemStack(String id); - - /** - * Place an item frame at the specified location - * Would remove the entity if it is not item frame - * @param location location - * @param id id - * @return item frame - */ - @Nullable - ItemFrame placeItemFrame(Location location, String id); - - /** - * Place an item display at the specified location - * Would remove the entity if it is not item display - * @param location location - * @param id id - * @return item display - */ - @Nullable - ItemDisplay placeItemDisplay(Location location, String id); - - /** - * Place custom note block at a specified location - * @param location location - * @param id id - */ - void placeNoteBlock(Location location, String id); - - /** - * Place custom string block at a specified location - * @param location location - * @param id id - */ - void placeTripWire(Location location, String id); - - /** - * Place custom chorus plant at a specified location - * @param location location - * @param id id - */ - void placeChorus(Location location, String id); - - /** - * Get the block id - * (Examples) - * Vanilla stone -> STONE - * ItemsAdder pot -> customcrops:pot - * Oraxen pot -> pot - * @param block block - * @return id - */ - @NotNull - String getBlockID(Block block); - - /** - * If an item exists in item library - * @param id id - * @return exists or not - */ - boolean doesItemExist(String id); - - /** - * Drop the block's loot - * @param block block - */ - void dropBlockLoot(Block block); - - /** - * Get the custom stuff at a specified location - * It might be a block or an entity - * @param location location - * @return id - */ - @NotNull - default String getAnyItemIDAt(Location location) { - String block = getBlockID(location.getBlock()); - if (!block.equals("AIR")) return block; - - String item_frame_id = getItemFrameIDAt(location); - if (item_frame_id != null) { - return item_frame_id; - } - - if (CustomCrops.getInstance().getVersionHelper().isVersionNewerThan1_19_R3()) { - String item_display_id = getItemDisplayIDAt(location); - if (item_display_id != null) { - return item_display_id; - } - } - return "AIR"; - } - - /** - * Remove all the custom stuff at a specified location - * @param location location - */ - default void removeCustomItemAt(Location location) { - removeAnyBlock(location); - removeItemFrame(location); - if (CustomCrops.getInstance().getVersionHelper().isVersionNewerThan1_19_R3()) { - removeItemDisplay(location); - } - } - - /** - * Get itemStack's internal id - * @param itemStack itemStack - * @return id - */ - @NotNull - String getItemStackID(@NotNull ItemStack itemStack); - - /** - * Get item display at a specified location - * This method would also remove overlapped entities - * @param location location - * @return id - */ - @Nullable - default String getItemDisplayIDAt(Location location) { - ItemDisplay itemDisplay = getItemDisplayAt(location); - if (itemDisplay == null) return null; - return getItemDisplayID(itemDisplay); - } - - /** - * Get item frame at a specified location - * This method would also remove overlapped entities - * @param location location - * @return id - */ - @Nullable - default String getItemFrameIDAt(Location location) { - ItemFrame itemFrame = getItemFrameAt(location); - if (itemFrame == null) return null; - return getItemFrameID(itemFrame); - } - - /** - * Get custom furniture's id - * @param itemDisplay itemDisplay - * @return id - */ - @Nullable - String getItemDisplayID(ItemDisplay itemDisplay); - - /** - * Get custom furniture's id - * @param itemFrame itemFrame - * @return id - */ - @Nullable - String getItemFrameID(ItemFrame itemFrame); - - /** - * Get item frame at a specified location - * This method would also remove overlapped entities - * @param location location - * @return id - */ - @Nullable - default ItemFrame getItemFrameAt(Location location) { - Collection itemFrames = location.clone().add(0.5,0.5,0.5).getNearbyEntitiesByType(ItemFrame.class, 0.4, 0.5, 0.4); - int i = itemFrames.size(); - int j = 1; - for (ItemFrame itemFrame : itemFrames) { - if (j != i) { - itemFrame.remove(); - j++; - } - else return itemFrame; - } - return null; - } - - /** - * Get item display at a specified location - * This method would also remove overlapped entities - * @param location location - * @return id - */ - @Nullable - default ItemDisplay getItemDisplayAt(Location location) { - Collection itemDisplays = location.clone().add(0.5,0,0.5).getNearbyEntitiesByType(ItemDisplay.class, 0.4, 0.8, 0.4); - int i = itemDisplays.size(); - int j = 1; - for (ItemDisplay itemDisplay : itemDisplays) { - if (j != i) { - itemDisplay.remove(); - j++; - } - else return itemDisplay; - } - return null; - } - - /** - * Remove item frames at a specified location - * @param location location - * @return success or not - */ - default boolean removeItemFrame(Location location) { - ItemFrame itemFrame = getItemFrameAt(location); - if (itemFrame != null) { - itemFrame.remove(); - if (CustomCrops.getInstance().getVersionHelper().isVersionNewerThan1_19_R3()) removeInteractions(location); - return true; - } - return false; - } - - /** - * Remove item display entities at a specified location - * @param location location - * @return success or not - */ - default boolean removeItemDisplay(Location location) { - ItemDisplay itemDisplay = getItemDisplayAt(location); - if (itemDisplay != null) { - itemDisplay.remove(); - removeInteractions(location); - return true; - } - return false; - } - - /** - * Remove interaction entities at a specified location - * @param location location - * @return success or not - */ - default boolean removeInteractions(Location location) { - Collection interactions = location.clone().add(0.5,0.5,0.5).getNearbyEntitiesByType(Interaction.class, 0.4, 0.5, 0.4); - for (Interaction interaction : interactions) { - interaction.remove(); - } - return interactions.size() != 0; - } - - /** - * If there's any custom stuff at a specified location - * @param location location - * @return has custom stuff or not - */ - default boolean detectAnyThing(Location location) { - Block block = location.getBlock(); - if (block.getType() != Material.AIR) return true; - Collection entities = location.clone().add(0.5,0.5,0.5).getNearbyEntitiesByType(ItemFrame.class, 0.4, 0.5, 0.4); - return entities.size() != 0 || (CustomCrops.getInstance().getVersionHelper().isVersionNewerThan1_19_R3() && detectItemDisplay(location)); - } - - /** - * If there's any item display entity at a specified location - * @param location location - * @return has item display or not - */ - default boolean detectItemDisplay(Location location) { - Collection entities = location.clone().add(0.5,0,0.5).getNearbyEntitiesByType(ItemDisplay.class, 0.4, 0.5, 0.4); - return entities.size() != 0; - } - - /** - * Place custom stuff according to its mode - * @param location location - * @param itemMode itemMode - */ - default void placeCustomItem(Location location, String id, ItemMode itemMode) { - if (itemMode == ItemMode.TRIPWIRE || itemMode == ItemMode.NOTE_BLOCK) - placeTripWire(location, id); - else if (itemMode == ItemMode.ITEM_FRAME) - placeItemFrame(location, id); - else if (itemMode == ItemMode.ITEM_DISPLAY) - placeItemDisplay(location, id); - else if (itemMode == ItemMode.CHORUS) - placeChorus(location, id); - } - - /** - * Remove custom stuff according to its mode - * @param location location - * @param itemMode itemMode - * @return success or not - */ - default boolean removeCustomItem(Location location, ItemMode itemMode) { - if (itemMode == ItemMode.TRIPWIRE || itemMode == ItemMode.CHORUS || itemMode == ItemMode.NOTE_BLOCK) - return removeCustomBlock(location); - else if (itemMode == ItemMode.ITEM_FRAME) - return removeItemFrame(location); - else if (itemMode == ItemMode.ITEM_DISPLAY) - return removeItemDisplay(location); - return false; - } - - /** - * Remove anything - * @param location location - */ - default void removeAnyThingAt(Location location) { - removeAnyBlock(location); - removeItemFrame(location); - if (CustomCrops.getInstance().getVersionHelper().isVersionNewerThan1_19_R3()) removeItemDisplay(location); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/PlatformManager.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/PlatformManager.java deleted file mode 100644 index 70cf144..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/PlatformManager.java +++ /dev/null @@ -1,1116 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.customplugin; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.event.*; -import net.momirealms.customcrops.api.object.BoneMeal; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.InteractCrop; -import net.momirealms.customcrops.api.object.action.Action; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.basic.MessageManager; -import net.momirealms.customcrops.api.object.crop.CropConfig; -import net.momirealms.customcrops.api.object.crop.GrowingCrop; -import net.momirealms.customcrops.api.object.crop.StageConfig; -import net.momirealms.customcrops.api.object.fertilizer.Fertilizer; -import net.momirealms.customcrops.api.object.fertilizer.FertilizerConfig; -import net.momirealms.customcrops.api.object.fill.PassiveFillMethod; -import net.momirealms.customcrops.api.object.fill.PositiveFillMethod; -import net.momirealms.customcrops.api.object.hologram.FertilizerHologram; -import net.momirealms.customcrops.api.object.hologram.WaterAmountHologram; -import net.momirealms.customcrops.api.object.pot.Pot; -import net.momirealms.customcrops.api.object.pot.PotConfig; -import net.momirealms.customcrops.api.object.requirement.CurrentState; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import net.momirealms.customcrops.api.object.sprinkler.Sprinkler; -import net.momirealms.customcrops.api.object.sprinkler.SprinklerConfig; -import net.momirealms.customcrops.api.object.wateringcan.WateringCanConfig; -import net.momirealms.customcrops.api.object.world.SimpleLocation; -import net.momirealms.customcrops.customplugin.itemsadder.ItemsAdderHandler; -import net.momirealms.customcrops.customplugin.oraxen.OraxenHandler; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.RotationUtils; -import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.Waterlogged; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.Player; -import org.bukkit.event.Cancellable; -import org.bukkit.event.Event; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -public class PlatformManager extends Function { - - private final CustomCrops plugin; - private final Handler handler; - private static final HashSet REPLACEABLE = new HashSet<>(Arrays.asList(Material.SNOW, Material.VINE, Material.GRASS, Material.TALL_GRASS, Material.SEAGRASS, Material.FERN, Material.LARGE_FERN, Material.AIR)); - - public PlatformManager(CustomCrops plugin) { - this.plugin = plugin; - this.handler = switch (plugin.getPlatform()) { - case ItemsAdder -> new ItemsAdderHandler(this); - case Oraxen -> new OraxenHandler(this); - }; - } - - @Override - public void load() { - this.handler.load(); - } - - @Override - public void unload() { - this.handler.unload(); - } - - public void onPlaceVanillaBlock(BlockPlaceEvent event) { - if (event.isCancelled()) return; - Block block = event.getBlock(); - onPlaceVanilla(event.getPlayer(), block.getLocation(), block.getType().name(), event); - } - - public void onBreakVanillaBlock(BlockBreakEvent event) { - if (event.isCancelled()) return; - Block block = event.getBlock(); - onBreakVanilla(event.getPlayer(), block.getLocation(), block.getType().name(), event); - } - - public void onPlaceFurniture(Player player, Location location, String id, Cancellable event) { - if (event != null && event.isCancelled()) return; - onPlaceCustom(player, location, id, event); - } - - public void onBreakFurniture(Player player, Entity entity, String id, Cancellable event) { - if (event.isCancelled()) return; - onBreakCustom(player, entity.getLocation().getBlock().getLocation(), id, event); - } - - public void onPlaceCustomBlock(Player player, Location location, String id, Cancellable event) { - if (event.isCancelled()) return; - onPlaceCustom(player, location, id, event); - } - - public void onBreakCustomBlock(Player player, Location location, String id, Cancellable event) { - if (event.isCancelled()) return; - onBreakCustom(player, location, id, event); - } - - public void onInteractBlock(PlayerInteractEvent event) { - if (event.getHand() != EquipmentSlot.HAND) return; - if (event.useItemInHand() == Event.Result.DENY) return; - if (event.getAction() == org.bukkit.event.block.Action.RIGHT_CLICK_AIR) { - onInteractAir(event.getPlayer()); - } else if (event.getAction() == org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) { - Block block = event.getClickedBlock(); - String id = plugin.getPlatformInterface().getBlockID(block); - assert block != null; - if (ConfigManager.enableCorruptionFixer && onInteractBrokenPot(id, block.getLocation())) { - return; - } - onInteractSomething(event.getPlayer(), block.getLocation(), id, event.getBlockFace(), event); - } - } - - public void onInteractFurniture(Player player, Entity entity, String id, Cancellable event) { - if (event.isCancelled()) return; - onInteractSomething(player, entity.getLocation().getBlock().getLocation(), id, null, event); - } - - public void onInteractAir(Player player) { - ItemStack item_in_hand = player.getInventory().getItemInMainHand(); - String id = plugin.getPlatformInterface().getItemStackID(item_in_hand); - onInteractWithWateringCan(player, id, item_in_hand, null, null); - } - - public void onBreakCustom(Player player, Location location, String id, Cancellable event) { - - if (onBreakGlass(player, id, location, event)) { - return; - } - - if (onBreakPot(player, id, location, event)) { - return; - } - - if (onBreakCrop(player, id, location, event)) { - return; - } - - if (onBreakSprinkler(player, id, location, event)) { - return; - } - - if (onBreakScarecrow(player, id, location, event)) { - return; - } - } - - public void onBreakVanilla(Player player, Location location, String id, Cancellable event) { - - if (onBreakGlass(player, id, location, event)) { - return; - } - - if (onBreakPot(player, id, location, event)) { - return; - } - - if (id.equals("NOTE_BLOCK")) { - SimpleLocation potLoc = SimpleLocation.getByBukkitLocation(location); - plugin.getWorldDataManager().removeCorrupted(potLoc); - plugin.getWorldDataManager().removePotData(potLoc); - } - } - - public void onPlaceCustom(Player player, Location location, String id, @Nullable Cancellable event) { - - if (onPlaceGlass(player, id, location, event)) { - return; - } - - if (onPlacePot(player, id, location, event)) { - return; - } - - if (onPlaceScarecrow(player, id, location, event)) { - return; - } - } - - public void onPlaceVanilla(Player player, Location location, String id, @Nullable Cancellable event) { - - if (onPlaceGlass(player, id, location, event)) { - return; - } - - if (onPlacePot(player, id, location, event)) { - return; - } - } - - void onInteractSomething(Player player, Location location, String id, @Nullable BlockFace blockFace, Cancellable event) { - - if (!plugin.getWorldDataManager().isWorldAllowed(location.getWorld())) { - return; - } - - ItemStack item_in_hand = player.getInventory().getItemInMainHand(); - String item_in_hand_id = plugin.getPlatformInterface().getItemStackID(item_in_hand); - - if (onInteractCrop(player, id, location, item_in_hand, item_in_hand_id, event)) { - return; - } - - if (onInteractWithSprinkler(player, location, item_in_hand, item_in_hand_id, blockFace)) { - return; - } - - if (onInteractSprinkler(player, id, location, item_in_hand, item_in_hand_id, event)) { - return; - } - - if (onInteractPot(player, id, location, item_in_hand, item_in_hand_id, event)) { - return; - } - - if (onInteractWithWateringCan(player, item_in_hand_id, item_in_hand, id, location)) { - return; - } - } - - /** - * Fix a pot if CustomCrops detected the location seems to be corrupted - */ - private boolean onInteractBrokenPot(String id, Location location) { - if (!id.equals("NOTE_BLOCK")) { - return false; - } - SimpleLocation simpleLocation = SimpleLocation.getByBukkitLocation(location); - String replacer; - String potKey = plugin.getWorldDataManager().removeCorrupted(simpleLocation); - Pot pot = plugin.getWorldDataManager().getPotData(simpleLocation); - if (pot != null) { - replacer = pot.isWet() ? pot.getConfig().getWetPot(pot.getFertilizer()) : pot.getConfig().getDryPot(pot.getFertilizer()); - } else { - if (potKey == null) { - return false; - } - PotConfig potConfig = plugin.getPotManager().getPotConfig(potKey); - if (potConfig == null) { - return true; - } - replacer = potConfig.getDryPot(null); - } - plugin.getPlatformInterface().placeNoteBlock(location, replacer); - furtherFix(location, 1); - return true; - } - - /** - * Fix the pots according to the range - * The scope of detection is to some extent determined by the scope of corruption - */ - public void furtherFix(Location location, int range) { - if (range >= ConfigManager.fixRange) return; - List locations = new ArrayList<>(); - for (int i = -range; i <= range; i++) { - for (int j = -range; j <= range; j++) { - if (i == -range || i == range || j == range || j == -range) { - locations.add(location.clone().add(i, 0, j)); - } - } - } - int success = 0; - for (Location check : locations) { - SimpleLocation simpleLocation = SimpleLocation.getByBukkitLocation(check); - String id = plugin.getPlatformInterface().getBlockID(check.getBlock()); - String potKey = plugin.getWorldDataManager().removeCorrupted(simpleLocation); - if (!id.equals("NOTE_BLOCK")) { - continue; - } - - String replacer; - Pot pot = plugin.getWorldDataManager().getPotData(simpleLocation); - if (pot != null) { - replacer = pot.isWet() ? pot.getConfig().getWetPot(pot.getFertilizer()) : pot.getConfig().getDryPot(pot.getFertilizer()); - } else { - if (potKey == null) { - continue; - } - PotConfig potConfig = plugin.getPotManager().getPotConfig(potKey); - if (potConfig == null) { - continue; - } - replacer = potConfig.getDryPot(null); - } - plugin.getPlatformInterface().placeNoteBlock(check, replacer); - success++; - } - - if (success != 0) { - furtherFix(location, range + 1); - } - } - - public boolean onBreakGlass(Player player, String id, Location location, Cancellable event) { - if (!id.equals(ConfigManager.greenhouseBlock)) { - return false; - } - - GreenhouseGlassBreakEvent greenhouseGlassBreakEvent = new GreenhouseGlassBreakEvent(player, location); - if (greenhouseGlassBreakEvent.isCancelled()) { - event.setCancelled(true); - return true; - } - - plugin.getWorldDataManager().removeGreenhouse(SimpleLocation.getByBukkitLocation(location)); - return true; - } - - public boolean onPlaceGlass(Player player, String id, Location location, Cancellable event) { - if (!id.equals(ConfigManager.greenhouseBlock)) { - return false; - } - - GreenhouseGlassPlaceEvent greenhouseGlassPlaceEvent = new GreenhouseGlassPlaceEvent(player, location); - Bukkit.getPluginManager().callEvent(greenhouseGlassPlaceEvent); - if (greenhouseGlassPlaceEvent.isCancelled()) { - if (event != null) event.setCancelled(true); - return true; - } - - plugin.getWorldDataManager().addGreenhouse(SimpleLocation.getByBukkitLocation(location)); - return true; - } - - public boolean onBreakScarecrow(Player player, String id, Location location, Cancellable event) { - if (!id.equals(ConfigManager.scarecrow)) { - return false; - } - - ScarecrowBreakEvent scarecrowBreakEvent = new ScarecrowBreakEvent(player, location); - Bukkit.getPluginManager().callEvent(scarecrowBreakEvent); - if (scarecrowBreakEvent.isCancelled()) { - event.setCancelled(true); - return true; - } - - plugin.getWorldDataManager().removeScarecrow(SimpleLocation.getByBukkitLocation(location)); - return true; - } - - public boolean onPlaceScarecrow(Player player, String id, Location location, Cancellable event) { - if (!id.equals(ConfigManager.scarecrow)) { - return false; - } - - ScarecrowPlaceEvent scarecrowPlaceEvent = new ScarecrowPlaceEvent(player, location); - Bukkit.getPluginManager().callEvent(scarecrowPlaceEvent); - if (scarecrowPlaceEvent.isCancelled()) { - if (event != null) event.setCancelled(true); - return true; - } - - plugin.getWorldDataManager().addScarecrow(SimpleLocation.getByBukkitLocation(location)); - return true; - } - - private boolean onPlacePot(Player player, String id, Location location, Cancellable event) { - PotConfig potConfig = plugin.getPotManager().getPotConfigByBlockID(id); - if (potConfig == null) return false; - - PotPlaceEvent potPlaceEvent = new PotPlaceEvent(player, location, potConfig.getKey()); - Bukkit.getPluginManager().callEvent(potPlaceEvent); - if (potPlaceEvent.isCancelled()) { - if (event != null) event.setCancelled(true); - } - - return true; - } - - public boolean onInteractSprinkler(Player player, String id, Location location, ItemStack item_in_hand, String item_in_hand_id, Cancellable event) { - SprinklerConfig sprinklerConfig = plugin.getSprinklerManager().getConfigByItemID(id); - if (sprinklerConfig == null) { - return false; - } - - if (!plugin.getAntiGriefLib().canPlace(player, location)) { - return true; - } - - SprinklerInteractEvent sprinklerInteractEvent = new SprinklerInteractEvent(player, item_in_hand, location, sprinklerConfig.getKey()); - Bukkit.getPluginManager().callEvent(sprinklerInteractEvent); - if (sprinklerInteractEvent.isCancelled()) { - event.setCancelled(true); - return true; - } - - outer: { - // water - PassiveFillMethod[] passiveFillMethods = sprinklerConfig.getPassiveFillMethods(); - for (PassiveFillMethod passiveFillMethod : passiveFillMethods) { - if (passiveFillMethod.isRightItem(item_in_hand_id)) { - SprinklerFillEvent sprinklerFillEvent = new SprinklerFillEvent(player, sprinklerConfig.getKey(), item_in_hand, passiveFillMethod.getAmount(), location); - Bukkit.getPluginManager().callEvent(sprinklerFillEvent); - if (sprinklerFillEvent.isCancelled()) { - return true; - } - - event.setCancelled(true); - doPassiveFillAction(player, item_in_hand, passiveFillMethod, location.clone().add(0,0.2,0)); - plugin.getWorldDataManager().addWaterToSprinkler(SimpleLocation.getByBukkitLocation(location), sprinklerFillEvent.getWater(), sprinklerConfig); - break outer; - } - } - - WateringCanConfig wateringCanConfig = plugin.getWateringCanManager().getConfigByItemID(item_in_hand_id); - if (wateringCanConfig != null) { - - if (!wateringCanConfig.canUse(player, location)) { - return true; - } - - String[] sprinkler_whitelist = wateringCanConfig.getSprinklerWhitelist(); - if (sprinkler_whitelist != null) { - inner: { - for (String sprinkler_allowed : sprinkler_whitelist) { - if (sprinkler_allowed.equals(plugin.getSprinklerManager().getConfigKeyByItemID(id))) { - break inner; - } - } - return true; - } - } - int current_water = plugin.getWateringCanManager().getCurrentWater(item_in_hand); - if (current_water <= 0) return true; - - SprinklerFillEvent sprinklerFillEvent = new SprinklerFillEvent(player, sprinklerConfig.getKey(), item_in_hand, 1, location); - Bukkit.getPluginManager().callEvent(sprinklerFillEvent); - if (sprinklerFillEvent.isCancelled()) { - return true; - } - - current_water--; - if (wateringCanConfig.hasActionBar()) { - AdventureUtils.playerActionbar(player, wateringCanConfig.getActionBarMsg(current_water)); - } - if (wateringCanConfig.getSound() != null) { - AdventureUtils.playerSound(player, wateringCanConfig.getSound()); - } - if (wateringCanConfig.getParticle() != null) { - location.getWorld().spawnParticle(wateringCanConfig.getParticle(), location.clone().add(0.5,0.4, 0.5),5,0.3,0.1,0.3); - } - - plugin.getWateringCanManager().setWater(item_in_hand, current_water, wateringCanConfig); - plugin.getWorldDataManager().addWaterToSprinkler(SimpleLocation.getByBukkitLocation(location), 1, sprinklerConfig); - break outer; - } - } - - Sprinkler sprinkler = plugin.getWorldDataManager().getSprinklerData(SimpleLocation.getByBukkitLocation(location)); - WaterAmountHologram waterAmountHologram = sprinklerConfig.getSprinklerHologram(); - if (waterAmountHologram != null) { - String content; - if (sprinkler != null) { - content = waterAmountHologram.getContent(sprinkler.getWater(), sprinklerConfig.getStorage()); - } else { - content = waterAmountHologram.getContent(0, sprinklerConfig.getStorage()); - } - plugin.getHologramManager().showHologram(player, - location.clone().add(0.5, waterAmountHologram.getOffset(),0.5), - AdventureUtils.getComponentFromMiniMessage(content), - waterAmountHologram.getDuration() * 1000, - waterAmountHologram.getMode(), - waterAmountHologram.getTextDisplayMeta() - ); - } - return true; - } - - private void doPassiveFillAction(Player player, ItemStack item_in_hand, PassiveFillMethod passiveFillMethod, Location location) { - if (player.getGameMode() != GameMode.CREATIVE) { - item_in_hand.setAmount(item_in_hand.getAmount() - 1); - ItemStack returned = passiveFillMethod.getReturnedItemStack(); - if (returned != null) { - player.getInventory().addItem(returned); - } - } - if (passiveFillMethod.getSound() != null) { - AdventureUtils.playerSound(player, passiveFillMethod.getSound()); - } - if (passiveFillMethod.getParticle() != null) { - location.getWorld().spawnParticle(passiveFillMethod.getParticle(), location.clone().add(0.5,0.3, 0.5),5,0.3,0.1,0.3); - } - } - - public boolean onInteractWithSprinkler(Player player, Location location, ItemStack item_in_hand, String item_in_hand_id, @Nullable BlockFace blockFace) { - SprinklerConfig sprinklerConfig = plugin.getSprinklerManager().getConfigByItemID(item_in_hand_id); - if (sprinklerConfig == null) { - return false; - } - - if (sprinklerConfig.getRequirements() != null) { - CurrentState currentState = new CurrentState(location, player); - for (Requirement requirement : sprinklerConfig.getRequirements()) { - if (!requirement.isConditionMet(currentState)) { - return true; - } - } - } - - if (blockFace != BlockFace.UP || REPLACEABLE.contains(location.getBlock().getType())) { - return true; - } - - if (!plugin.getAntiGriefLib().canPlace(player, location)) { - return true; - } - - Location sprinkler_loc = location.clone().add(0,1,0); - if (plugin.getPlatformInterface().detectAnyThing(sprinkler_loc)) return true; - - SprinklerPlaceEvent sprinklerPlaceEvent = new SprinklerPlaceEvent(player, item_in_hand, sprinkler_loc, sprinklerConfig.getKey()); - Bukkit.getPluginManager().callEvent(sprinklerPlaceEvent); - if (sprinklerPlaceEvent.isCancelled()) { - return true; - } - - if (player.getGameMode() != GameMode.CREATIVE) item_in_hand.setAmount(item_in_hand.getAmount() - 1); - CustomCrops.getInstance().getPlatformInterface().placeCustomItem(sprinkler_loc, sprinklerConfig.getThreeD(), sprinklerConfig.getItemMode()); - - if (sprinklerConfig.isInfinite()) { - plugin.getWorldDataManager().addWaterToSprinkler(SimpleLocation.getByBukkitLocation(location), -1, sprinklerConfig); - } - - if (sprinklerConfig.getSound() != null) { - AdventureUtils.playerSound(player, sprinklerConfig.getSound()); - } - return true; - } - - public boolean onInteractCrop(Player player, String id, Location location, ItemStack item_in_hand, String item_in_hand_id, Cancellable event) { - CropConfig cropConfig = plugin.getCropManager().getCropConfigByStage(id); - if (cropConfig == null) { - return false; - } - - StageConfig stageConfig = plugin.getCropManager().getStageConfig(id); - if (stageConfig == null) { - return true; - } - - if (!plugin.getAntiGriefLib().canBreak(player, location)) { - return true; - } - - CropInteractEvent cropInteractEvent = new CropInteractEvent(player, item_in_hand, location, id, cropConfig.getKey()); - Bukkit.getPluginManager().callEvent(cropInteractEvent); - if (cropInteractEvent.isCancelled()) { - event.setCancelled(true); - return true; - } - - if (item_in_hand_id.equals("AIR")) { - InteractCrop interactCrop = stageConfig.getInteractByHand(); - if (interactCrop != null) { - if (!interactCrop.canInteract(player, location)) - return true; - for (Action action : interactCrop.getActions()) - action.doOn(player, SimpleLocation.getByBukkitLocation(location), cropConfig.getCropMode()); - } - return true; - } - - Location bottomLoc = location.clone().subtract(0, 1, 0); - String bottomBlockID = plugin.getPlatformInterface().getBlockID(bottomLoc.getBlock()); - String pot_id = plugin.getPotManager().getPotKeyByBlockID(bottomBlockID); - if (pot_id != null) { - PotConfig potConfig = plugin.getPotManager().getPotConfig(pot_id); - if (potConfig != null) { - PassiveFillMethod[] passiveFillMethods = potConfig.getPassiveFillMethods(); - if (passiveFillMethods != null) { - for (PassiveFillMethod passiveFillMethod : passiveFillMethods) { - if (passiveFillMethod.isRightItem(item_in_hand_id)) { - - PotWaterEvent potWaterEvent = new PotWaterEvent(player, item_in_hand, passiveFillMethod.getAmount(), bottomLoc); - Bukkit.getPluginManager().callEvent(potWaterEvent); - if (potWaterEvent.isCancelled()) { - return true; - } - - event.setCancelled(true); - doPassiveFillAction(player, item_in_hand, passiveFillMethod, location); - plugin.getWorldDataManager().addWaterToPot(SimpleLocation.getByBukkitLocation(bottomLoc), potWaterEvent.getWater(), pot_id); - return true; - } - } - } - - WateringCanConfig wateringCanConfig = plugin.getWateringCanManager().getConfigByItemID(item_in_hand_id); - if (wateringCanConfig != null) { - - if (!wateringCanConfig.canUse(player, location)) - return true; - - if (!wateringCanConfig.isWhiteListedPot(pot_id)) - return true; - - int current_water = plugin.getWateringCanManager().getCurrentWater(item_in_hand); - if (current_water <= 0) return true; - - PotWaterEvent potWaterEvent = new PotWaterEvent(player, item_in_hand, 1, bottomLoc); - Bukkit.getPluginManager().callEvent(potWaterEvent); - if (potWaterEvent.isCancelled()) - return true; - - current_water--; - this.waterPot(wateringCanConfig.getWidth(), wateringCanConfig.getLength(), bottomLoc, player.getLocation().getYaw(), pot_id, wateringCanConfig.getParticle(), potWaterEvent.getWater()); - this.doWateringCanActions(player, item_in_hand, wateringCanConfig, current_water); - return true; - } - } - } - - BoneMeal[] boneMeals = cropConfig.getBoneMeals(); - if (boneMeals != null) { - for (BoneMeal boneMeal : boneMeals) { - if (boneMeal.isRightItem(item_in_hand_id)) { - if (plugin.getWorldDataManager().addCropPointAt(SimpleLocation.getByBukkitLocation(location), boneMeal.getPoint())) { - if (player.getGameMode() != GameMode.CREATIVE) { - item_in_hand.setAmount(item_in_hand.getAmount() - 1); - if (boneMeal.getReturned() != null) { - player.getInventory().addItem(boneMeal.getReturned()); - } - } - if (boneMeal.getParticle() != null) { - location.getWorld().spawnParticle(boneMeal.getParticle(), location.clone().add(0.5,0.5, 0.5),3,0.4,0.4,0.4); - } - if (boneMeal.getSound() != null) { - AdventureUtils.playerSound(player, boneMeal.getSound()); - } - } - return true; - } - } - } - - InteractCrop[] interactActions = stageConfig.getInteractCropWithItem(); - if (interactActions != null) { - for (InteractCrop interactCrop : interactActions) { - if (interactCrop.isRightItem(item_in_hand_id)) { - if (!interactCrop.canInteract(player, location)) - continue; - if (player.getGameMode() != GameMode.CREATIVE) { - if (interactCrop.isConsumed()) - item_in_hand.setAmount(item_in_hand.getAmount() - 1); - if (interactCrop.getReturned() != null) - player.getInventory().addItem(interactCrop.getReturned()); - } - Action[] inAc = interactCrop.getActions(); - if (inAc != null) { - for (Action action : inAc) { - action.doOn( - player, - SimpleLocation.getByBukkitLocation(location), - cropConfig.getCropMode() - ); - } - } - return true; - } - } - } - return true; - } - - public boolean onInteractPot(Player player, String id, Location location, ItemStack item_in_hand, String item_in_hand_id, Cancellable event) { - String pot_id = plugin.getPotManager().getPotKeyByBlockID(id); - if (pot_id == null) - return false; - - PotConfig potConfig = plugin.getPotManager().getPotConfig(pot_id); - if (potConfig == null) - return false; - - if (!plugin.getAntiGriefLib().canPlace(player, location)) - return true; - - PotInteractEvent potInteractEvent = new PotInteractEvent(player, item_in_hand, location, pot_id); - Bukkit.getPluginManager().callEvent(potInteractEvent); - if (potInteractEvent.isCancelled()) - return true; - - outer: { - // plant - CropConfig cropConfig = plugin.getCropManager().getCropConfigBySeed(item_in_hand_id); - if (cropConfig != null) { - String[] pot_whitelist = cropConfig.getPotWhitelist(); - inner: { - for (String bottom_block : pot_whitelist) { - if (bottom_block.equals(pot_id)) { - break inner; - } - } - AdventureUtils.playerMessage(player, MessageManager.prefix + MessageManager.unsuitablePot); - return true; - } - - Location crop_loc = location.clone().add(0,1,0); - if (!cropConfig.canPlant(player, crop_loc)) - return true; - - if (plugin.getPlatformInterface().detectAnyThing(crop_loc)) - return true; - - if (ConfigManager.enableLimitation && plugin.getWorldDataManager().getChunkCropAmount(SimpleLocation.getByBukkitLocation(crop_loc)) >= plugin.getConfigManager().getCropLimit(location.getWorld().getName())) { - AdventureUtils.playerMessage(player, MessageManager.prefix + MessageManager.reachChunkLimit); - return true; - } - - String crop_model = Objects.requireNonNull(cropConfig.getStageConfig(0)).getModel(); - CropPlantEvent cropPlantEvent = new CropPlantEvent(player, item_in_hand, location, cropConfig.getKey(), 0, crop_model); - Bukkit.getPluginManager().callEvent(cropPlantEvent); - if (cropPlantEvent.isCancelled()) - return true; - - switch (cropConfig.getCropMode()) { - case ITEM_DISPLAY -> { - ItemDisplay itemDisplay = CustomCrops.getInstance().getPlatformInterface().placeItemDisplay(crop_loc, cropPlantEvent.getCropModel()); - if (itemDisplay == null) - return true; - if (cropConfig.isRotationEnabled()) - itemDisplay.setRotation(RotationUtils.getRandomFloatRotation(), itemDisplay.getLocation().getPitch()); - } - case ITEM_FRAME -> { - ItemFrame itemFrame = CustomCrops.getInstance().getPlatformInterface().placeItemFrame(crop_loc, cropPlantEvent.getCropModel()); - if (itemFrame == null) - return true; - if (cropConfig.isRotationEnabled()) - itemFrame.setRotation(RotationUtils.getRandomRotation()); - } - case TRIPWIRE -> CustomCrops.getInstance().getPlatformInterface().placeTripWire(crop_loc, cropPlantEvent.getCropModel()); - } - - Action[] plantActions = cropConfig.getPlantActions(); - if (plantActions != null) { - for (Action action : plantActions) { - action.doOn( - player, - SimpleLocation.getByBukkitLocation(crop_loc), - cropConfig.getCropMode() - ); - } - } - player.swingMainHand(); - if (player.getGameMode() != GameMode.CREATIVE) item_in_hand.setAmount(item_in_hand.getAmount() - 1); - plugin.getWorldDataManager().addCropData(SimpleLocation.getByBukkitLocation(crop_loc), new GrowingCrop(cropConfig.getKey(), cropPlantEvent.getPoint()), true); - return true; - } - - // water - PassiveFillMethod[] passiveFillMethods = potConfig.getPassiveFillMethods(); - if (passiveFillMethods != null) { - for (PassiveFillMethod passiveFillMethod : passiveFillMethods) { - if (passiveFillMethod.isRightItem(item_in_hand_id)) { - - PotWaterEvent potWaterEvent = new PotWaterEvent(player, item_in_hand, passiveFillMethod.getAmount(), location); - Bukkit.getPluginManager().callEvent(potWaterEvent); - if (potWaterEvent.isCancelled()) - return true; - - event.setCancelled(true); - doPassiveFillAction(player, item_in_hand, passiveFillMethod, location.clone().add(0,1,0)); - plugin.getWorldDataManager().addWaterToPot(SimpleLocation.getByBukkitLocation(location), potWaterEvent.getWater(), pot_id); - break outer; - } - } - } - - // use watering can - WateringCanConfig wateringCanConfig = plugin.getWateringCanManager().getConfigByItemID(item_in_hand_id); - if (wateringCanConfig != null) { - - if (!wateringCanConfig.canUse(player, location)) - return true; - - if (!wateringCanConfig.isWhiteListedPot(pot_id)) - return true; - - int current_water = plugin.getWateringCanManager().getCurrentWater(item_in_hand); - if (current_water <= 0) return true; - - PotWaterEvent potWaterEvent = new PotWaterEvent(player, item_in_hand, 1, location); - Bukkit.getPluginManager().callEvent(potWaterEvent); - if (potWaterEvent.isCancelled()) - return true; - - current_water--; - this.waterPot(wateringCanConfig.getWidth(), wateringCanConfig.getLength(), location, player.getLocation().getYaw(), pot_id, wateringCanConfig.getParticle(), potWaterEvent.getWater()); - this.doWateringCanActions(player, item_in_hand, wateringCanConfig, current_water); - break outer; - } - - // use fertilizer - FertilizerConfig fertilizerConfig = plugin.getFertilizerManager().getConfigByItemID(item_in_hand_id); - if (fertilizerConfig != null) { - - if (!fertilizerConfig.canUse(player, location)) - return true; - - FertilizerUseEvent fertilizerUseEvent = new FertilizerUseEvent(player, item_in_hand, fertilizerConfig.getKey(), location); - Bukkit.getPluginManager().callEvent(fertilizerUseEvent); - if (fertilizerUseEvent.isCancelled()) - return true; - - if (fertilizerConfig.isBeforePlant() && plugin.getCropManager().containsStage(plugin.getPlatformInterface().getAnyItemIDAt(location.clone().add(0,1,0)))) { - AdventureUtils.playerMessage(player, MessageManager.prefix + MessageManager.beforePlant); - return true; - } - - if (player.getGameMode() != GameMode.CREATIVE) item_in_hand.setAmount(item_in_hand.getAmount() - 1); - player.swingMainHand(); - - if (fertilizerConfig.getSound() != null) { - AdventureUtils.playerSound(player, fertilizerConfig.getSound()); - } - if (fertilizerConfig.getParticle() != null) { - location.getWorld().spawnParticle(fertilizerConfig.getParticle(), location.clone().add(0.5,1.1,0.5), 5,0.25,0.1,0.25, 0); - } - plugin.getWorldDataManager().addFertilizerToPot(SimpleLocation.getByBukkitLocation(location), new Fertilizer(fertilizerConfig), pot_id); - break outer; - } - } - - Pot potData = Optional.ofNullable(plugin.getWorldDataManager().getPotData(SimpleLocation.getByBukkitLocation(location))).orElse(new Pot(pot_id, null, 0)); - GrowingCrop growingCrop = plugin.getWorldDataManager().getCropData(SimpleLocation.getByBukkitLocation(location)); - PotInfoEvent potInfoEvent = new PotInfoEvent(player, location, item_in_hand, potData.getFertilizer(), potData.getWater(), growingCrop); - Bukkit.getPluginManager().callEvent(potInfoEvent); - - if (potConfig.getPotInfoItem() != null && !item_in_hand_id.equals(potConfig.getPotInfoItem())) { - return true; - } - - WaterAmountHologram waterAmountHologram = potConfig.getWaterAmountHologram(); - if (waterAmountHologram != null) { - double offset = 0; - StageConfig stageConfig = plugin.getCropManager().getStageConfig(plugin.getPlatformInterface().getAnyItemIDAt(location.clone().add(0,1,0))); - if (stageConfig != null) { - offset = stageConfig.getOffsetCorrection(); - } - plugin.getHologramManager().showHologram(player, - location.clone().add(0.5,waterAmountHologram.getOffset() + offset,0.5), - AdventureUtils.getComponentFromMiniMessage(waterAmountHologram.getContent(potData.getWater(), potConfig.getMaxStorage())), - waterAmountHologram.getDuration() * 1000, - waterAmountHologram.getMode(), - waterAmountHologram.getTextDisplayMeta() - ); - } - - FertilizerHologram fertilizerHologram = potConfig.getFertilizerHologram(); - if (fertilizerHologram != null && potData.getFertilizer() != null) { - double offset = 0; - StageConfig stageConfig = plugin.getCropManager().getStageConfig(plugin.getPlatformInterface().getAnyItemIDAt(location.clone().add(0,1,0))); - if (stageConfig != null) { - offset = stageConfig.getOffsetCorrection(); - } - plugin.getHologramManager().showHologram(player, - location.clone().add(0.5,fertilizerHologram.getOffset() + offset,0.5), - AdventureUtils.getComponentFromMiniMessage(fertilizerHologram.getContent(potData.getFertilizer())), - fertilizerHologram.getDuration() * 1000, - fertilizerHologram.getMode(), - fertilizerHologram.getTextDisplayMeta() - ); - } - return true; - } - - private void doWateringCanActions(Player player, ItemStack item_in_hand, WateringCanConfig wateringCanConfig, int current_water) { - if (wateringCanConfig.hasActionBar()) { - AdventureUtils.playerActionbar(player, wateringCanConfig.getActionBarMsg(current_water)); - } - if (wateringCanConfig.getSound() != null) { - AdventureUtils.playerSound(player, wateringCanConfig.getSound()); - } - plugin.getWateringCanManager().setWater(item_in_hand, current_water, wateringCanConfig); - } - - public boolean onBreakPot(@Nullable Entity entity, String id, Location location, Cancellable event) { - PotConfig potConfig = plugin.getPotManager().getPotConfigByBlockID(id); - if (potConfig == null) { - return false; - } - - PotBreakEvent potBreakEvent = new PotBreakEvent(entity, location, potConfig.getKey()); - Bukkit.getPluginManager().callEvent(potBreakEvent); - if (potBreakEvent.isCancelled()) { - event.setCancelled(true); - return true; - } - - Location above_loc = location.clone().add(0,1,0); - String above_id = plugin.getPlatformInterface().getAnyItemIDAt(above_loc); - // has item above - // is a crop - if (onBreakCrop(entity, above_id, above_loc, event)) { - // The event might be cancelled if the player doesn't meet the break requirements - if (event.isCancelled()) { - return true; - } - plugin.getPlatformInterface().removeCustomItemAt(above_loc); - } - - plugin.getWorldDataManager().removePotData(SimpleLocation.getByBukkitLocation(location)); - return true; - } - - private boolean onBreakSprinkler(Player player, String id, Location location, Cancellable event) { - SprinklerConfig sprinklerConfig = plugin.getSprinklerManager().getConfigByItemID(id); - if (sprinklerConfig == null) { - return false; - } - - SprinklerBreakEvent sprinklerBreakEvent = new SprinklerBreakEvent(player, location, sprinklerConfig.getKey()); - Bukkit.getPluginManager().callEvent(sprinklerBreakEvent); - if (sprinklerBreakEvent.isCancelled()) { - event.setCancelled(true); - return true; - } - - plugin.getWorldDataManager().removeSprinklerData(SimpleLocation.getByBukkitLocation(location)); - return true; - } - - private boolean onBreakCrop(@Nullable Entity entity, String id, Location location, Cancellable event) { - if (plugin.getCropManager().isDeadCrop(id)) { - return true; - } - - CropConfig cropConfig = plugin.getCropManager().getCropConfigByStage(id); - if (cropConfig == null) return false; - - // The entity might be other creatures when the pot is FARMLAND - // because of vanilla farmland mechanics - if (entity instanceof Player player) { - - if (!cropConfig.canBreak(player, location)) { - event.setCancelled(true); - return true; - } - - CropBreakEvent cropBreakEvent = new CropBreakEvent(entity, cropConfig.getKey(), id, location); - Bukkit.getPluginManager().callEvent(cropBreakEvent); - if (cropBreakEvent.isCancelled()) - return true; - - if (player.getGameMode() != GameMode.CREATIVE) { - StageConfig stageConfig = plugin.getCropManager().getStageConfig(id); - if (stageConfig != null) { - Action[] breakActions = stageConfig.getBreakActions(); - if (breakActions != null) { - for (Action action : breakActions) { - action.doOn(player, SimpleLocation.getByBukkitLocation(location), cropConfig.getCropMode()); - } - } - } - } - } else { - CropBreakEvent cropBreakEvent = new CropBreakEvent(entity, cropConfig.getKey(), id, location); - Bukkit.getPluginManager().callEvent(cropBreakEvent); - if (cropBreakEvent.isCancelled()) - return true; - - StageConfig stageConfig = plugin.getCropManager().getStageConfig(id); - if (stageConfig != null) { - Action[] breakActions = stageConfig.getBreakActions(); - if (breakActions != null) { - for (Action action : breakActions) { - action.doOn(null, SimpleLocation.getByBukkitLocation(location), cropConfig.getCropMode()); - } - } - } - } - plugin.getWorldDataManager().removeCropData(SimpleLocation.getByBukkitLocation(location)); - return true; - } - - private void waterPot(int width, int length, Location location, float yaw, String id, @Nullable Particle particle, int water){ - int extend = width / 2; - if (yaw < 45 && yaw > -135) { - if (yaw > -45) { - for (int i = -extend; i <= extend; i++) { - Location tempLoc = location.clone().add(i, 0, -1); - for (int j = 0; j < length; j++){ - tempLoc.add(0,0,1); - tryToWaterPot(tempLoc, id, particle, water); - } - } - } else { - for (int i = -extend; i <= extend; i++) { - Location tempLoc = location.clone().add(-1, 0, i); - for (int j = 0; j < length; j++){ - tempLoc.add(1,0,0); - tryToWaterPot(tempLoc, id, particle, water); - } - } - } - } else { - if (yaw > 45 && yaw < 135) { - for (int i = -extend; i <= extend; i++) { - Location tempLoc = location.clone().add(1, 0, i); - for (int j = 0; j < length; j++){ - tempLoc.subtract(1,0,0); - tryToWaterPot(tempLoc, id, particle, water); - } - } - } else { - for (int i = -extend; i <= extend; i++) { - Location tempLoc = location.clone().add(i, 0, 1); - for (int j = 0; j < length; j++){ - tempLoc.subtract(0,0,1); - tryToWaterPot(tempLoc, id, particle, water); - } - } - } - } - } - - private void tryToWaterPot(Location location, String pot_id, @Nullable Particle particle, int water) { - String blockID = plugin.getPlatformInterface().getBlockID(location.getBlock()); - String current_id = plugin.getPotManager().getPotKeyByBlockID(blockID); - if (current_id != null && current_id.equals(pot_id)) { - plugin.getWorldDataManager().addWaterToPot(SimpleLocation.getByBukkitLocation(location), water, pot_id); - if (particle != null) - location.getWorld().spawnParticle(particle, location.clone().add(0.5,1, 0.5),3,0.1,0.1,0.1); - } - } - - public boolean onInteractWithWateringCan(Player player, String item_in_hand_id, ItemStack item_in_hand, @Nullable String id, @Nullable Location location) { - WateringCanConfig wateringCanConfig = plugin.getWateringCanManager().getConfigByItemID(item_in_hand_id); - if (wateringCanConfig == null) { - return false; - } - - int current = plugin.getWateringCanManager().getCurrentWater(item_in_hand); - if (current >= wateringCanConfig.getStorage()) return true; - - int add = 0; - - outer: { - if (id != null && location != null) { - if (!plugin.getAntiGriefLib().canPlace(player, location)) - return true; - if (!wateringCanConfig.canUse(player, location)) - return true; - for (PositiveFillMethod positiveFillMethod : wateringCanConfig.getPositiveFillMethods()) { - if (positiveFillMethod.getId().equals(id)) { - add = positiveFillMethod.getAmount(); - if (positiveFillMethod.getSound() != null) { - AdventureUtils.playerSound(player, positiveFillMethod.getSound()); - } - if (positiveFillMethod.getParticle() != null) { - location.getWorld().spawnParticle(positiveFillMethod.getParticle(), location.clone().add(0.5,1.1, 0.5),5,0.1,0.1,0.1); - } - break outer; - } - } - } - - Block targetBlock = player.getTargetBlockExact(5, FluidCollisionMode.ALWAYS); - if (targetBlock == null) - return true; - String blockId = plugin.getPlatformInterface().getBlockID(targetBlock); - if (targetBlock.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { - blockId = "WATER"; - } - - for (PositiveFillMethod positiveFillMethod : wateringCanConfig.getPositiveFillMethods()) { - if (positiveFillMethod.getId().equals(blockId)) { - if (!plugin.getAntiGriefLib().canPlace(player, targetBlock.getLocation())) - return true; - if (!wateringCanConfig.canUse(player, location)) - return true; - add = positiveFillMethod.getAmount(); - if (positiveFillMethod.getSound() != null) - AdventureUtils.playerSound(player, positiveFillMethod.getSound()); - if (positiveFillMethod.getParticle() != null) - targetBlock.getWorld().spawnParticle(positiveFillMethod.getParticle(), targetBlock.getLocation().add(0.5,1.1, 0.5),5,0.1,0.1,0.1); - break; - } - } - } - - if (add == 0) return true; - int finalWater = Math.min(wateringCanConfig.getStorage(), add + current); - plugin.getWateringCanManager().setWater(item_in_hand, finalWater, wateringCanConfig); - if (wateringCanConfig.hasActionBar()) { - AdventureUtils.playerActionbar(player, wateringCanConfig.getActionBarMsg(finalWater)); - } - return true; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/crucible/CrucibleHandler.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/crucible/CrucibleHandler.java deleted file mode 100644 index d1ab08a..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/crucible/CrucibleHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -///* -// * Copyright (C) <2022> -// * -// * 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 -// * 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 . -// */ -// -//package net.momirealms.customcrops.api.customplugin.crucible; -// -//import net.momirealms.customcrops.api.customplugin.Handler; -//import net.momirealms.customcrops.api.customplugin.PlatformManager; -// -//@Deprecated -//public class CrucibleHandler extends Handler { -// -// public CrucibleHandler(PlatformManager platformManager) { -// super(platformManager); -// //TODO NO API Events Available -// } -//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/crucible/CruciblePluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/crucible/CruciblePluginImpl.java deleted file mode 100644 index e1814db..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/crucible/CruciblePluginImpl.java +++ /dev/null @@ -1,154 +0,0 @@ -///* -// * Copyright (C) <2022> -// * -// * 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 -// * 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 . -// */ -// -//package net.momirealms.customcrops.api.customplugin.crucible; -// -//import io.lumine.mythic.bukkit.BukkitAdapter; -//import io.lumine.mythiccrucible.MythicCrucible; -//import io.lumine.mythiccrucible.items.CrucibleItem; -//import io.lumine.mythiccrucible.items.blocks.CustomBlockItemContext; -//import net.momirealms.customcrops.api.customplugin.PlatformInterface; -//import net.momirealms.customcrops.api.util.AdventureUtils; -//import org.bukkit.Location; -//import org.bukkit.block.Block; -//import org.bukkit.block.BlockFace; -//import org.bukkit.entity.ItemDisplay; -//import org.bukkit.entity.ItemFrame; -//import org.bukkit.inventory.ItemStack; -//import org.jetbrains.annotations.NotNull; -//import org.jetbrains.annotations.Nullable; -// -//import java.util.Optional; -// -//@Deprecated -//public class CruciblePluginImpl implements PlatformInterface { -// -// @Override -// public boolean removeCustomBlock(Location location) { -// Optional optionalCB = MythicCrucible.inst().getItemManager().getCustomBlockManager().getBlockFromBlock(location.getBlock()); -// if (optionalCB.isEmpty()) return false; -// optionalCB.get().remove(location.getBlock(), null, false); -// return true; -// } -// -// @Nullable -// @Override -// public String getCustomBlockID(Location location) { -// Optional optionalCB = MythicCrucible.inst().getItemManager().getCustomBlockManager().getBlockFromBlock(location.getBlock()); -// if (optionalCB.isEmpty()) return null; -// else return optionalCB.get().getCrucibleItem().getInternalName(); -// } -// -// @Nullable -// @Override -// public ItemStack getItemStack(String id) { -// Optional optionalCI = MythicCrucible.inst().getItemManager().getItem(id); -// if (optionalCI.isEmpty()) return null; -// else return BukkitAdapter.adapt(optionalCI.get().getMythicItem().generateItemStack(1)); -// } -// -// @Nullable -// @Override -// @Deprecated -// public ItemFrame placeItemFrame(Location location, String id) { -// Optional optionalCI = MythicCrucible.inst().getItemManager().getItem(id); -// if (optionalCI.isPresent()) { -// optionalCI.get().getFurnitureData().place(location.getBlock(), BlockFace.UP, 0); -// } else { -// AdventureUtils.consoleMessage("[CustomCrop] ItemFrame not exists: " + id); -// } -// //TODO API limits (It's using private methods to place) -// return null; -// } -// -// @Nullable -// @Override -// @Deprecated -// public ItemDisplay placeItemDisplay(Location location, String id) { -// //TODO Not implemented feature -// return null; -// } -// -// @Override -// public void placeNoteBlock(Location location, String id) { -// Optional optionalCI = MythicCrucible.inst().getItemManager().getItem(id); -// if (optionalCI.isPresent()) { -// location.getBlock().setBlockData(optionalCI.get().getBlockData().getBlockData()); -// } else { -// AdventureUtils.consoleMessage("[CustomCrop] NoteBlock not exists: " + id); -// } -// } -// -// @Override -// public void placeTripWire(Location location, String id) { -// Optional optionalCI = MythicCrucible.inst().getItemManager().getItem(id); -// if (optionalCI.isPresent()) { -// location.getBlock().setBlockData(optionalCI.get().getBlockData().getBlockData()); -// } else { -// AdventureUtils.consoleMessage("[CustomCrop] Tirpwire not exists: " + id); -// } -// } -// -// @NotNull -// @Override -// public String getBlockID(Block block) { -// Optional optionalCB = MythicCrucible.inst().getItemManager().getCustomBlockManager().getBlockFromBlock(block); -// if (optionalCB.isEmpty()) return block.getType().name(); -// else return optionalCB.get().getCrucibleItem().getInternalName(); -// } -// -// @Override -// public boolean doesItemExist(String id) { -// Optional optionalCI = MythicCrucible.inst().getItemManager().getItem(id); -// return optionalCI.isPresent(); -// } -// -// @Override -// @Deprecated -// public void dropBlockLoot(Block block) { -// //TODO -// } -// -// @Override -// @Deprecated -// public void placeChorus(Location location, String id) { -// //TODO -// } -// -// @NotNull -// @Override -// public String getItemStackID(@NotNull ItemStack itemStack) { -// Optional optionalCI = MythicCrucible.inst().getItemManager().getItem(itemStack); -// if (optionalCI.isEmpty()) return itemStack.getType().name(); -// else return optionalCI.get().getInternalName(); -// } -// -// @Nullable -// @Override -// @Deprecated -// public String getItemDisplayID(ItemDisplay itemDisplay) { -// return null; -// } -// -// @Nullable -// @Override -// public String getItemFrameID(ItemFrame itemFrame) { -// Optional optionalCI = MythicCrucible.inst().getItemManager().getFurnitureManager().getItemFromFrame(itemFrame); -// if (optionalCI.isEmpty()) return null; -// else return optionalCI.get().getInternalName(); -// } -//} diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/itemsadder/ItemsAdderHandler.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/itemsadder/ItemsAdderHandler.java deleted file mode 100644 index 90263d9..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/itemsadder/ItemsAdderHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.customplugin.itemsadder; - -import dev.lone.itemsadder.api.Events.*; -import net.momirealms.customcrops.customplugin.Handler; -import net.momirealms.customcrops.customplugin.PlatformManager; -import org.bukkit.event.EventHandler; - -public class ItemsAdderHandler extends Handler { - - public ItemsAdderHandler(PlatformManager platformManager) { - super(platformManager); - } - - @EventHandler - public void onBreakCustomBlock(CustomBlockBreakEvent event) { - platformManager.onBreakCustomBlock(event.getPlayer(), event.getBlock().getLocation(), event.getNamespacedID(), event); - } - - @EventHandler - public void onBreakFurniture(FurnitureBreakEvent event) { - platformManager.onBreakFurniture(event.getPlayer(), event.getBukkitEntity(), event.getNamespacedID(), event); - } - - @EventHandler - public void onPlaceFurniture(FurniturePlaceSuccessEvent event) { - platformManager.onPlaceFurniture(event.getPlayer(), event.getBukkitEntity().getLocation().getBlock().getLocation(), event.getNamespacedID(), null); - } - - @EventHandler - public void onPlaceCustomBlock(CustomBlockPlaceEvent event) { - platformManager.onPlaceCustomBlock(event.getPlayer(), event.getBlock().getLocation(), event.getNamespacedID(), event); - } - - @EventHandler - public void onInteractFurniture(FurnitureInteractEvent event) { - platformManager.onInteractFurniture(event.getPlayer(), event.getBukkitEntity(), event.getNamespacedID(), event); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/itemsadder/ItemsAdderPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/itemsadder/ItemsAdderPluginImpl.java deleted file mode 100644 index e706e6f..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/itemsadder/ItemsAdderPluginImpl.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.customplugin.itemsadder; - -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTItem; -import dev.lone.itemsadder.api.CustomBlock; -import dev.lone.itemsadder.api.CustomFurniture; -import dev.lone.itemsadder.api.CustomStack; -import net.momirealms.customcrops.customplugin.PlatformInterface; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.ItemFrame; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class ItemsAdderPluginImpl implements PlatformInterface { - - @Override - public boolean removeCustomBlock(Location location) { - return CustomBlock.remove(location); - } - - @Nullable - @Override - public String getCustomBlockID(Location location) { - CustomBlock customBlock = CustomBlock.byAlreadyPlaced(location.getBlock()); - return customBlock == null ? null : customBlock.getNamespacedID(); - } - - @Nullable - @Override - public ItemStack getItemStack(String id) { - CustomStack customStack = CustomStack.getInstance(id); - return customStack == null ? null : customStack.getItemStack(); - } - - @Nullable - @Override - public ItemFrame placeItemFrame(Location location, String id) { - CustomFurniture customFurniture = CustomFurniture.spawn(id, location.getBlock()); - if (customFurniture == null) { - AdventureUtils.consoleMessage("[CustomCrops] Furniture not exists: " + id); - return null; - } - Entity entity = customFurniture.getArmorstand(); - if (entity instanceof ItemFrame itemFrame) - return itemFrame; - else { - AdventureUtils.consoleMessage("[CustomCrops] ItemFrame not placed: " + id + ". " + - "If you are sure that you are using the right item type, " + - "please set max max-furniture-vehicles-per-chunk to a higher value in IA config.yml."); - customFurniture.remove(false); - } - return null; - } - - @Nullable - @Override - public ItemDisplay placeItemDisplay(Location location, String id) { - CustomFurniture customFurniture = CustomFurniture.spawn(id, location.getBlock()); - if (customFurniture == null) { - AdventureUtils.consoleMessage("[CustomCrops] Furniture not exists: " + id); - return null; - } - Entity entity = customFurniture.getArmorstand(); - if (entity instanceof ItemDisplay itemDisplay) - return itemDisplay; - else { - AdventureUtils.consoleMessage("[CustomCrops] ItemFrame not placed: " + id + ". " + - "If you are sure that you are using the right item type, " + - "please set max max-furniture-vehicles-per-chunk to a higher value in IA config.yml."); - customFurniture.remove(false); - } - return null; - } - - @Override - public void placeNoteBlock(Location location, String id) { - CustomBlock customBlock = CustomBlock.place(id, location); - if (customBlock == null) { - AdventureUtils.consoleMessage("[CustomCrops] NoteBlock not exists: " + id); - } - } - - @Override - public void placeTripWire(Location location, String id) { - CustomBlock customBlock = CustomBlock.place(id, location); - if (customBlock == null) { - AdventureUtils.consoleMessage("[CustomCrops] Tripwire not exists: " + id); - } - } - - @NotNull - @Override - public String getBlockID(Block block) { - CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); - return customBlock == null ? block.getType().name() : customBlock.getNamespacedID(); - } - - @Override - public boolean doesItemExist(String id) { - return CustomStack.getInstance(id) != null; - } - - @Override - public void dropBlockLoot(Block block) { - CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); - if (customBlock == null) return; - Location block_loc = block.getLocation(); - for (ItemStack itemStack : customBlock.getLoot()) { - block_loc.getWorld().dropItemNaturally(block_loc, itemStack); - } - } - - @Override - public void placeChorus(Location location, String id) { - CustomBlock customBlock = CustomBlock.place(id, location); - if (customBlock == null) { - AdventureUtils.consoleMessage("[CustomCrops] Chorus not exists: " + id); - } - } - - @NotNull - @Override - public String getItemStackID(@NotNull ItemStack itemStack) { - if (itemStack.getType() != Material.AIR) { - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound nbtCompound = nbtItem.getCompound("itemsadder"); - if (nbtCompound != null) return nbtCompound.getString("namespace") + ":" + nbtCompound.getString("id"); - } - return itemStack.getType().name(); - } - - @Nullable - @Override - public String getItemDisplayID(ItemDisplay itemDisplay) { - CustomFurniture customFurniture = CustomFurniture.byAlreadySpawned(itemDisplay); - if (customFurniture == null) return null; - return customFurniture.getNamespacedID(); - } - - @Nullable - @Override - public String getItemFrameID(ItemFrame itemFrame) { - CustomFurniture customFurniture = CustomFurniture.byAlreadySpawned(itemFrame); - if (customFurniture == null) return null; - return customFurniture.getNamespacedID(); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/oraxen/OraxenHandler.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/oraxen/OraxenHandler.java deleted file mode 100644 index b9e2bd8..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/oraxen/OraxenHandler.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.customplugin.oraxen; - -import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; -import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; -import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; -import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockBreakEvent; -import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockPlaceEvent; -import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockBreakEvent; -import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockPlaceEvent; -import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; -import io.th0rgal.oraxen.mechanics.provided.gameplay.noteblock.NoteBlockMechanic; -import io.th0rgal.oraxen.mechanics.provided.gameplay.stringblock.StringBlockMechanic; -import net.momirealms.customcrops.customplugin.Handler; -import net.momirealms.customcrops.customplugin.PlatformManager; -import org.bukkit.entity.Entity; -import org.bukkit.event.EventHandler; - -public class OraxenHandler extends Handler { - - public OraxenHandler(PlatformManager platformManager) { - super(platformManager); - } - - @EventHandler - public void onBreakNoteBlock(OraxenNoteBlockBreakEvent event) { - platformManager.onBreakCustomBlock(event.getPlayer(), event.getBlock().getLocation(), event.getMechanic().getItemID(), event); - } - - @EventHandler - public void onBreakStringBlock(OraxenStringBlockBreakEvent event) { - platformManager.onBreakCustomBlock(event.getPlayer(), event.getBlock().getLocation(), event.getMechanic().getItemID(), event); - } - - @EventHandler - public void onBreakFurniture(OraxenFurnitureBreakEvent event) { - Entity entity = event.getBaseEntity(); - // TODO Why entity would sometimes be null - if (entity == null) return; - platformManager.onBreakFurniture(event.getPlayer(), entity, event.getMechanic().getItemID(), event); - } - - @EventHandler - public void onPlaceFurniture(OraxenFurniturePlaceEvent event) { - FurnitureMechanic mechanic = event.getMechanic(); - if (mechanic == null) return; - platformManager.onPlaceFurniture(event.getPlayer(), event.getBaseEntity().getLocation().getBlock().getLocation(), mechanic.getItemID(), event); - } - - @EventHandler - public void onPlaceStringBlock(OraxenStringBlockPlaceEvent event) { - StringBlockMechanic mechanic = event.getMechanic(); - if (mechanic == null) return; - platformManager.onPlaceCustomBlock(event.getPlayer(), event.getBlock().getLocation(), mechanic.getItemID(), event); - } - - @EventHandler - public void onPlaceNoteBlock(OraxenNoteBlockPlaceEvent event) { - NoteBlockMechanic mechanic = event.getMechanic(); - // TODO Why mechanic would sometimes be null - if (mechanic == null) return; - platformManager.onPlaceCustomBlock(event.getPlayer(), event.getBlock().getLocation(), mechanic.getItemID(), event); - } - - @EventHandler - public void onInteractFurniture(OraxenFurnitureInteractEvent event) { - platformManager.onInteractFurniture(event.getPlayer(), event.getBaseEntity(), event.getMechanic().getItemID(), event); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/customplugin/oraxen/OraxenPluginImpl.java b/plugin/src/main/java/net/momirealms/customcrops/customplugin/oraxen/OraxenPluginImpl.java deleted file mode 100644 index a95d84d..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/customplugin/oraxen/OraxenPluginImpl.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.customplugin.oraxen; - -import de.tr7zw.changeme.nbtapi.NBTCompound; -import de.tr7zw.changeme.nbtapi.NBTItem; -import io.th0rgal.oraxen.api.OraxenBlocks; -import io.th0rgal.oraxen.api.OraxenFurniture; -import io.th0rgal.oraxen.api.OraxenItems; -import io.th0rgal.oraxen.items.ItemBuilder; -import io.th0rgal.oraxen.mechanics.Mechanic; -import io.th0rgal.oraxen.mechanics.provided.gameplay.block.BlockMechanic; -import io.th0rgal.oraxen.mechanics.provided.gameplay.block.BlockMechanicFactory; -import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureFactory; -import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; -import io.th0rgal.oraxen.mechanics.provided.gameplay.noteblock.NoteBlockMechanicFactory; -import io.th0rgal.oraxen.mechanics.provided.gameplay.stringblock.StringBlockMechanicFactory; -import io.th0rgal.oraxen.utils.drops.Drop; -import net.momirealms.customcrops.customplugin.PlatformInterface; -import net.momirealms.customcrops.util.AdventureUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Rotation; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.Entity; -import org.bukkit.entity.ItemDisplay; -import org.bukkit.entity.ItemFrame; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class OraxenPluginImpl implements PlatformInterface { - - @Override - public boolean removeCustomBlock(Location location) { - return OraxenBlocks.remove(location, null); - } - - @Nullable - @Override - public String getCustomBlockID(Location location) { - Mechanic mechanic = OraxenBlocks.getOraxenBlock(location); - return mechanic == null ? null : mechanic.getItemID(); - } - - @Nullable - @Override - public ItemStack getItemStack(String id) { - ItemBuilder itemBuilder = OraxenItems.getItemById(id); - return itemBuilder == null ? null : itemBuilder.build(); - } - - @Nullable - @Override - public ItemFrame placeItemFrame(Location location, String id) { - FurnitureMechanic mechanic = (FurnitureMechanic) FurnitureFactory.getInstance().getMechanic(id); - if (mechanic == null) { - AdventureUtils.consoleMessage("[CustomCrops] Furniture not exists: " + id); - return null; - } - Entity entity = mechanic.place(location, 0f, BlockFace.UP); - if (entity instanceof ItemFrame itemFrame) - return itemFrame; - else { - AdventureUtils.consoleMessage("[CustomCrops] ItemFrame not exists: " + id); - // use oraxen method to remove sub entities - OraxenFurniture.remove(entity, null); - return null; - } - } - - @Nullable - @Override - public ItemDisplay placeItemDisplay(Location location, String id) { -// FurnitureMechanic mechanic = (FurnitureMechanic) FurnitureFactory.getInstance().getMechanic(id); -// if (mechanic == null) { -// AdventureUtils.consoleMessage("[CustomCrops] Furniture not exists: " + id); -// return null; -// } -// Entity entity = mechanic.place(location); -// if (entity instanceof ItemDisplay itemDisplay) -// return itemDisplay; -// else { -// AdventureUtils.consoleMessage("[CustomCrops] ItemDisplay not exists: " + id); -// // use oraxen method to remove sub entities -// OraxenFurniture.remove(entity, null); -// return null; -// } - - Entity entity = OraxenFurniture.place(id, location, Rotation.NONE, BlockFace.UP); - if (entity instanceof ItemDisplay itemDisplay) - return itemDisplay; - else { - AdventureUtils.consoleMessage("[CustomCrops] ItemDisplay not exists: " + id); - OraxenFurniture.remove(entity, null); - return null; - } - } - - @Override - public void placeNoteBlock(Location location, String id) { - try { - NoteBlockMechanicFactory.setBlockModel(location.getBlock(), id); - } catch (NullPointerException e) { - AdventureUtils.consoleMessage("[CustomCrop] NoteBlock not exists: " + id); - } - } - - @Override - public void placeTripWire(Location location, String id) { - try { - StringBlockMechanicFactory.setBlockModel(location.getBlock(), id); - } catch (NullPointerException e) { - AdventureUtils.consoleMessage("[CustomCrop] Tripwire not exists: " + id); - } - } - - @NotNull - @Override - public String getBlockID(Block block) { - Mechanic mechanic = OraxenBlocks.getOraxenBlock(block.getBlockData()); - return mechanic == null ? block.getType().name() : mechanic.getItemID(); - } - - @Override - public boolean doesItemExist(String id) { - return OraxenItems.getItemById(id) != null; - } - - @Override - public void dropBlockLoot(Block block) { - BlockMechanic mechanic = BlockMechanicFactory.getBlockMechanic(block); - if (mechanic == null) return; - Drop drop = mechanic.getDrop(); - if (drop != null) - drop.spawns(block.getLocation(), new ItemStack(Material.AIR)); - } - - @Override - public void placeChorus(Location location, String id) { - StringBlockMechanicFactory.setBlockModel(location.getBlock(), id); - } - - @Nullable - @Override - public String getItemDisplayID(ItemDisplay itemDisplay) { - FurnitureMechanic furnitureMechanic = OraxenFurniture.getFurnitureMechanic(itemDisplay); - if (furnitureMechanic != null) { - return furnitureMechanic.getItemID(); - } - return null; - } - - @Nullable - @Override - public String getItemFrameID(ItemFrame itemFrame) { - FurnitureMechanic furnitureMechanic = OraxenFurniture.getFurnitureMechanic(itemFrame); - if (furnitureMechanic != null) { - return furnitureMechanic.getItemID(); - } - return null; - } - - @NotNull - @Override - public String getItemStackID(@NotNull ItemStack itemStack) { - if (itemStack.getType() != Material.AIR) { - NBTItem nbtItem = new NBTItem(itemStack); - NBTCompound bukkitPublic = nbtItem.getCompound("PublicBukkitValues"); - if (bukkitPublic != null) { - String id = bukkitPublic.getString("oraxen:id"); - if (!id.equals("")) return id; - } - } - return itemStack.getType().name(); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/helper/LibraryLoader.java b/plugin/src/main/java/net/momirealms/customcrops/helper/LibraryLoader.java deleted file mode 100644 index db32c78..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/helper/LibraryLoader.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * This file is part of helper, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.momirealms.customcrops.helper; - -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; -import net.momirealms.customcrops.CustomCrops; - -import java.io.File; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.util.Objects; -import java.util.StringJoiner; - -/** - * Resolves {@link MavenLibrary} annotations for a class, and loads the dependency - * into the classloader. - */ -public final class LibraryLoader { - - @SuppressWarnings("Guava") - private static final Supplier URL_INJECTOR = Suppliers.memoize(() -> URLClassLoaderAccess.create((URLClassLoader) CustomCrops.getInstance().getClass().getClassLoader())); - - /** - * Resolves all {@link MavenLibrary} annotations on the given object. - * - * @param object the object to load libraries for. - */ - public static void loadAll(Object object) { - loadAll(object.getClass()); - } - - /** - * Resolves all {@link MavenLibrary} annotations on the given class. - * - * @param clazz the class to load libraries for. - */ - public static void loadAll(Class clazz) { - MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class); - for (MavenLibrary lib : libs) { - load(lib.groupId(), lib.artifactId(), lib.version(), lib.repo().url()); - } - } - - public static void load(String groupId, String artifactId, String version, String repoUrl) { - load(new Dependency(groupId, artifactId, version, repoUrl)); - } - - public static void load(Dependency d) { - //Log.info(String.format("Loading dependency %s:%s:%s from %s", d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl())); - String name = d.artifactId() + "-" + d.version(); - - File saveLocation = new File(getLibFolder(d), name + ".jar"); - if (!saveLocation.exists()) { - - try { - Log.info("Dependency '" + name + "' is not already in the libraries folder. Attempting to download..."); - URL url = d.getUrl(); - - try (InputStream is = url.openStream()) { - Files.copy(is, saveLocation.toPath()); - Log.info("Dependency '" + name + "' successfully downloaded."); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (!saveLocation.exists()) { - throw new RuntimeException("Unable to download dependency: " + d.toString()); - } - - try { - URL_INJECTOR.get().addURL(saveLocation.toURI().toURL()); - } catch (Exception e) { - throw new RuntimeException("Unable to load dependency: " + saveLocation.toString(), e); - } - } - - private static File getLibFolder(Dependency dependency) { - File pluginDataFolder = CustomCrops.getInstance().getDataFolder(); - File serverDir = pluginDataFolder.getParentFile().getParentFile(); - - File helperDir = new File(serverDir, "libraries"); - String[] split = dependency.groupId().split("\\."); - File jarDir; - StringJoiner stringJoiner = new StringJoiner(File.separator); - for (String str : split) { - stringJoiner.add(str); - } - jarDir = new File(helperDir, stringJoiner + File.separator + dependency.artifactId + File.separator + dependency.version); - jarDir.mkdirs(); - return jarDir; - } - - public record Dependency(String groupId, String artifactId, String version, String repoUrl) { - public Dependency(String groupId, String artifactId, String version, String repoUrl) { - this.groupId = Objects.requireNonNull(groupId, "groupId"); - this.artifactId = Objects.requireNonNull(artifactId, "artifactId"); - this.version = Objects.requireNonNull(version, "version"); - this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl"); - } - - public URL getUrl() throws MalformedURLException { - String repo = this.repoUrl; - if (!repo.endsWith("/")) { - repo += "/"; - } - repo += "%s/%s/%s/%s-%s.jar"; - - String url = String.format(repo, this.groupId.replace(".", "/"), this.artifactId, this.version, this.artifactId, this.version); - return new URL(url); - } - - @Override - public boolean equals(Object o) { - if (o == this) return true; - if (!(o instanceof final Dependency other)) return false; - return this.groupId().equals(other.groupId()) && - this.artifactId().equals(other.artifactId()) && - this.version().equals(other.version()) && - this.repoUrl().equals(other.repoUrl()); - } - - @Override - public int hashCode() { - final int PRIME = 59; - int result = 1; - result = result * PRIME + this.groupId().hashCode(); - result = result * PRIME + this.artifactId().hashCode(); - result = result * PRIME + this.version().hashCode(); - result = result * PRIME + this.repoUrl().hashCode(); - return result; - } - - @Override - public String toString() { - return "LibraryLoader.Dependency(" + - "groupId=" + this.groupId() + ", " + - "artifactId=" + this.artifactId() + ", " + - "version=" + this.version() + ", " + - "repoUrl=" + this.repoUrl() + ")"; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/IntegrationManager.java b/plugin/src/main/java/net/momirealms/customcrops/integration/IntegrationManager.java deleted file mode 100644 index 6350f0a..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/IntegrationManager.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.integration; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.integration.item.DefaultImpl; -import net.momirealms.customcrops.integration.item.MMOItemsItemImpl; -import net.momirealms.customcrops.integration.item.MythicMobsItemImpl; -import net.momirealms.customcrops.integration.item.NeigeItemsImpl; -import net.momirealms.customcrops.integration.job.EcoJobsImpl; -import net.momirealms.customcrops.integration.job.JobsRebornImpl; -import net.momirealms.customcrops.integration.papi.PlaceholderManager; -import net.momirealms.customcrops.integration.quest.BattlePassCCQuest; -import net.momirealms.customcrops.integration.quest.BetonQuestCCQuest; -import net.momirealms.customcrops.integration.quest.ClueScrollCCQuest; -import net.momirealms.customcrops.integration.season.AdvancedSeasonsImpl; -import net.momirealms.customcrops.integration.season.CustomCropsSeasonImpl; -import net.momirealms.customcrops.integration.season.RealisticSeasonsImpl; -import net.momirealms.customcrops.integration.skill.AureliumsImpl; -import net.momirealms.customcrops.integration.skill.EcoSkillsImpl; -import net.momirealms.customcrops.integration.skill.MMOCoreImpl; -import net.momirealms.customcrops.integration.skill.mcMMOImpl; -import net.momirealms.customcrops.util.AdventureUtils; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.PluginManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; - -public class IntegrationManager extends Function { - - private final CustomCrops plugin; - private SkillInterface skillInterface; - private JobInterface jobInterface; - private ItemInterface[] itemInterfaces; - private SeasonInterface seasonInterface; - private final PluginManager pluginManager; - private VaultHook vaultHook; - private final PlaceholderManager placeholderManager; - - public IntegrationManager(CustomCrops plugin) { - this.plugin = plugin; - this.pluginManager = Bukkit.getPluginManager(); - this.placeholderManager = new PlaceholderManager(plugin); - this.registerQuests(); - if (Bukkit.getPluginManager().isPluginEnabled("Vault")) { - this.vaultHook = new VaultHook(); - if (!vaultHook.initialize()) { - AdventureUtils.consoleMessage("[CustomCrops] Failed to initialize Vault"); - } - } - } - - @Override - public void load() { - this.hookJobs(); - this.hookSkills(); - this.hookSeasons(); - this.hookItems(); - this.placeholderManager.load(); - } - - @Override - public void unload() { - this.seasonInterface = null; - this.skillInterface = null; - this.itemInterfaces = null; - this.jobInterface = null; - this.placeholderManager.unload(); - } - - private void hookItems() { - ArrayList itemInterfaceList = new ArrayList<>(); - if (pluginManager.isPluginEnabled("MythicMobs")) { - itemInterfaceList.add(new MythicMobsItemImpl()); - hookMessage("MythicMobs"); - } - if (pluginManager.isPluginEnabled("MMOItems")) { - itemInterfaceList.add(new MMOItemsItemImpl()); - hookMessage("MMOItems"); - } - if (pluginManager.isPluginEnabled("NeigeItems")) { - itemInterfaceList.add(new NeigeItemsImpl()); - hookMessage("NeigeItems"); - } - itemInterfaceList.add(new DefaultImpl()); - this.itemInterfaces = itemInterfaceList.toArray(new ItemInterface[0]); - } - - private void hookSeasons() { - if (pluginManager.isPluginEnabled("RealisticSeasons")) { - this.seasonInterface = new RealisticSeasonsImpl(); - ConfigManager.rsHook = true; - hookMessage("RealisticSeasons"); - } else if (pluginManager.isPluginEnabled("AdvancedSeasons")) { - this.seasonInterface = new AdvancedSeasonsImpl(); - ConfigManager.rsHook = true; - hookMessage("AdvancedSeasons"); - } else { - this.seasonInterface = new CustomCropsSeasonImpl(); - } - } - - private void hookJobs() { - if (this.jobInterface instanceof JobsRebornImpl jobsReborn) { - HandlerList.unregisterAll(jobsReborn); - } - if (pluginManager.isPluginEnabled("Jobs")) { - this.jobInterface = new JobsRebornImpl(); - Bukkit.getPluginManager().registerEvents((Listener) jobInterface, plugin); - hookMessage("JobsReborn"); - } else if (pluginManager.isPluginEnabled("EcoJobs")) { - this.jobInterface = new EcoJobsImpl(); - hookMessage("EcoJobs"); - } - } - - private void hookSkills() { - if (pluginManager.isPluginEnabled("mcMMO")) { - this.skillInterface = new mcMMOImpl(); - hookMessage("mcMMO"); - } else if (pluginManager.isPluginEnabled("MMOCore")) { - this.skillInterface = new MMOCoreImpl(ConfigUtils.getConfig("config.yml").getString("other-settings.MMOCore-profession-name", "farmer")); - hookMessage("MMOCore"); - } else if (pluginManager.isPluginEnabled("AureliumSkills")) { - this.skillInterface = new AureliumsImpl(); - hookMessage("AureliumSkills"); - } else if (pluginManager.isPluginEnabled("EcoSkills")) { - this.skillInterface = new EcoSkillsImpl(); - hookMessage("EcoSkills"); - } - } - - private void hookMessage(String plugin){ - AdventureUtils.consoleMessage("[CustomCrops] " + plugin + " hooked!"); - } - - @NotNull - public ItemStack build(String key) { - return build(key, null); - } - - @NotNull - public ItemStack build(String key, Player player) { - if (key != null) { - for (ItemInterface itemInterface : itemInterfaces) { - ItemStack itemStack = itemInterface.build(key, player); - if (itemStack != null) { - return itemStack; - } - } - } - return new ItemStack(Material.AIR); - } - - private void registerQuests() { - PluginManager pluginManager = Bukkit.getPluginManager(); - if (pluginManager.isPluginEnabled("ClueScrolls")) { - ClueScrollCCQuest quest = new ClueScrollCCQuest(plugin); - Bukkit.getPluginManager().registerEvents(quest, plugin); - hookMessage("ClueScrolls"); - } - if (pluginManager.isPluginEnabled("BetonQuest")) { - BetonQuestCCQuest.register(); - hookMessage("BetonQuest"); - } - if (pluginManager.isPluginEnabled("BattlePass")) { - BattlePassCCQuest battlePassCCQuest = new BattlePassCCQuest(); - Bukkit.getPluginManager().registerEvents(battlePassCCQuest, plugin); - BattlePassCCQuest.register(); - hookMessage("BattlePass"); - } - } - - @Nullable - public SkillInterface getSkillInterface() { - return skillInterface; - } - - @NotNull - public SeasonInterface getSeasonInterface() { - return seasonInterface; - } - - @Nullable - public JobInterface getJobInterface() { - return jobInterface; - } - - public PlaceholderManager getPlaceholderManager() { - return placeholderManager; - } - - @Nullable - public VaultHook getVault() { - return vaultHook; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/item/NeigeItemsImpl.java b/plugin/src/main/java/net/momirealms/customcrops/integration/item/NeigeItemsImpl.java deleted file mode 100644 index 9fbf1b3..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/item/NeigeItemsImpl.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.momirealms.customcrops.integration.item; - -import net.momirealms.customcrops.integration.ItemInterface; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; -import pers.neige.neigeitems.manager.ItemManager; - -public class NeigeItemsImpl implements ItemInterface { - - @Override - public @Nullable ItemStack build(String material, Player player) { - if (!material.startsWith("NeigeItems:")) return null; - material = material.substring(11); - return ItemManager.INSTANCE.getItemStack(material, player); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/job/JobsRebornImpl.java b/plugin/src/main/java/net/momirealms/customcrops/integration/job/JobsRebornImpl.java deleted file mode 100644 index 4010195..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/job/JobsRebornImpl.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.integration.job; - -import com.gamingmesh.jobs.Jobs; -import com.gamingmesh.jobs.container.*; -import com.gamingmesh.jobs.listeners.JobsPaymentListener; -import net.momirealms.customcrops.api.event.CropBreakEvent; -import net.momirealms.customcrops.integration.JobInterface; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class JobsRebornImpl implements JobInterface, Listener { - - @Override - public void addXp(Player player, double amount, String jobName) { - if (jobName == null) jobName = "Farmer"; - JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); - if (jobsPlayer != null) { - List jobs = jobsPlayer.getJobProgression(); - Job job = Jobs.getJob(jobName); - for (JobProgression progression : jobs) { - if (progression.getJob().equals(job)) { - progression.addExperience(amount); - break; - } - } - } - } - - @Override - public int getLevel(Player player, @Nullable String jobName) { - if (jobName == null) jobName = "Farmer"; - JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); - if (jobsPlayer != null) { - List jobs = jobsPlayer.getJobProgression(); - Job job = Jobs.getJob(jobName); - for (JobProgression progression : jobs) { - if (progression.getJob().equals(job)) { - return progression.getLevel(); - } - } - } - return 0; - } - - @EventHandler - public void onHarvest(CropBreakEvent event) { - if (!Jobs.getGCManager().canPerformActionInWorld(event.getEntity().getWorld())) return; - if (!(event.getEntity() instanceof Player player)) return; - - // check if in creative - if (!JobsPaymentListener.payIfCreative(player)) - return; - - if (!Jobs.getPermissionHandler().hasWorldPermission(player, player.getLocation().getWorld().getName())) - return; - - JobsPlayer jobsPlayer = Jobs.getPlayerManager().getJobsPlayer(player); - if (jobsPlayer == null) return; - - Jobs.action(jobsPlayer, new CustomCropsInfo(event.getCropItemID(), ActionType.MMKILL)); - } - - public static class CustomCropsInfo extends BaseActionInfo { - private final String name; - - public CustomCropsInfo(String name, ActionType type) { - super(type); - this.name = name; - } - - public String getName() { - return this.name; - } - - public String getNameWithSub() { - return this.name; - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/papi/PlaceholderManager.java b/plugin/src/main/java/net/momirealms/customcrops/integration/papi/PlaceholderManager.java deleted file mode 100644 index 3840b48..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/papi/PlaceholderManager.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.integration.papi; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -public class PlaceholderManager extends Function { - - private SeasonPapi seasonPapi; - private boolean hasPapi; - - public PlaceholderManager(CustomCrops plugin) { - this.hasPapi = false; - if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { - this.seasonPapi = new SeasonPapi(plugin); - this.hasPapi = true; - } - } - - @Override - public void load() { - if (seasonPapi != null) seasonPapi.register(); - } - - @Override - public void unload() { - if (seasonPapi != null) seasonPapi.unregister(); - } - - public String parse(Player player, String text) { - return hasPapi ? ParseUtil.setPlaceholders(player, text) : text; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/papi/SeasonPapi.java b/plugin/src/main/java/net/momirealms/customcrops/integration/papi/SeasonPapi.java deleted file mode 100644 index c35a802..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/papi/SeasonPapi.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.integration.papi; - -import me.clip.placeholderapi.expansion.PlaceholderExpansion; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class SeasonPapi extends PlaceholderExpansion { - - private final CustomCrops plugin; - - public SeasonPapi(CustomCrops plugin) { - this.plugin = plugin; - } - - @Override - public @NotNull String getIdentifier() { - return "customcrops"; - } - - @Override - public @NotNull String getAuthor() { - return "XiaoMoMi"; - } - - @Override - public @NotNull String getVersion() { - return "3.0"; - } - - @Override - public boolean persist() { - return true; - } - - @Override - public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) { - String[] split = params.split("_", 2); - if (split.length == 1) { - switch (split[0]) { - case "season" -> { - Player online_player = player.getPlayer(); - if (online_player == null) return null; - return plugin.getIntegrationManager().getSeasonInterface().getSeason(player.getPlayer().getWorld().getName()).getDisplay(); - } - case "date" -> { - Player online_player = player.getPlayer(); - if (online_player == null) return null; - return String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(player.getPlayer().getWorld().getName())); - } - case "daysleft" -> { - Player online_player = player.getPlayer(); - if (online_player == null) return null; - return String.valueOf(1 + ConfigManager.seasonInterval - plugin.getIntegrationManager().getSeasonInterface().getDate(player.getPlayer().getWorld().getName())); - } - } - } else if (split.length == 2) { - switch (split[0]) { - case "season" -> { - return plugin.getIntegrationManager().getSeasonInterface().getSeason(split[1]).getDisplay(); - } - case "date" -> { - return String.valueOf(plugin.getIntegrationManager().getSeasonInterface().getDate(split[1])); - } - case "daysleft" -> { - return String.valueOf(1 + ConfigManager.seasonInterval - plugin.getIntegrationManager().getSeasonInterface().getDate(split[1])); - } - } - } - return null; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/quest/BattlePassCCQuest.java b/plugin/src/main/java/net/momirealms/customcrops/integration/quest/BattlePassCCQuest.java deleted file mode 100644 index 242b66c..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/quest/BattlePassCCQuest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.integration.quest; - -import io.github.battlepass.BattlePlugin; -import io.github.battlepass.api.events.server.PluginReloadEvent; -import net.advancedplugins.bp.impl.actions.ActionRegistry; -import net.advancedplugins.bp.impl.actions.external.executor.ActionQuestExecutor; -import net.momirealms.customcrops.api.event.CropBreakEvent; -import net.momirealms.customcrops.api.event.CropPlantEvent; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.plugin.java.JavaPlugin; - -public class BattlePassCCQuest implements Listener { - - public static void register() { - ActionRegistry actionRegistry = BattlePlugin.getPlugin().getActionRegistry(); - actionRegistry.hook("customcrops", CropQuest::new); - } - - @EventHandler(ignoreCancelled = true) - public void onBattlePassReload(PluginReloadEvent event) { - register(); - } - - private static class CropQuest extends ActionQuestExecutor { - - public CropQuest(JavaPlugin battlePlugin) { - super(battlePlugin, "customcrops"); - } - - @EventHandler - public void onHarvest(CropBreakEvent event) { - if (event.isCancelled()) - return; - if (event.getEntity() instanceof Player player) { - String id = event.getCropItemID(); - String[] split = id.split(":"); - this.executionBuilder("harvest") - .player(player) - .root(split[split.length - 1]) - .progress(1) - .buildAndExecute(); - } - } - - @EventHandler - public void onPlant(CropPlantEvent event) { - if (event.isCancelled()) - return; - String id = event.getCropKey(); - this.executionBuilder("plant") - .player(event.getPlayer()) - .root(id) - .progress(1) - .buildAndExecute(); - } - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/quest/BetonQuestCCQuest.java b/plugin/src/main/java/net/momirealms/customcrops/integration/quest/BetonQuestCCQuest.java deleted file mode 100644 index a7f2802..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/quest/BetonQuestCCQuest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.integration.quest; - -import net.momirealms.customcrops.api.event.CropBreakEvent; -import net.momirealms.customcrops.util.AdventureUtils; -import org.betonquest.betonquest.BetonQuest; -import org.betonquest.betonquest.Instruction; -import org.betonquest.betonquest.VariableNumber; -import org.betonquest.betonquest.api.CountingObjective; -import org.betonquest.betonquest.api.profiles.OnlineProfile; -import org.betonquest.betonquest.api.profiles.Profile; -import org.betonquest.betonquest.exceptions.InstructionParseException; -import org.betonquest.betonquest.utils.PlayerConverter; -import org.betonquest.betonquest.utils.location.CompoundLocation; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; - -import java.util.Collections; -import java.util.HashSet; - -public class BetonQuestCCQuest extends CountingObjective implements Listener { - - private final CompoundLocation playerLocation; - private final VariableNumber rangeVar; - private final HashSet crop_ids; - - public BetonQuestCCQuest(Instruction instruction) throws InstructionParseException { - super(instruction, "crop_to_harvest"); - crop_ids = new HashSet<>(); - Collections.addAll(crop_ids, instruction.getArray()); - targetAmount = instruction.getVarNum(); - preCheckAmountNotLessThanOne(targetAmount); - final String pack = instruction.getPackage().getQuestPath(); - final String loc = instruction.getOptional("playerLocation"); - final String range = instruction.getOptional("range"); - if (loc != null && range != null) { - playerLocation = new CompoundLocation(pack, loc); - rangeVar = new VariableNumber(pack, range); - } else { - playerLocation = null; - rangeVar = null; - } - } - - public static void register() { - BetonQuest.getInstance().registerObjectives("customcrops", BetonQuestCCQuest.class); - } - - @EventHandler - public void onHarvest(CropBreakEvent event) { - if (!(event.getEntity() instanceof Player player)) return; - OnlineProfile onlineProfile = PlayerConverter.getID(player); - if (!containsPlayer(onlineProfile)) { - return; - } - if (isInvalidLocation(event, onlineProfile)) { - return; - } - if (this.crop_ids.contains(event.getCropItemID()) && this.checkConditions(onlineProfile)) { - getCountingData(onlineProfile).progress(1); - completeIfDoneOrNotify(onlineProfile); - } - } - - private boolean isInvalidLocation(CropBreakEvent event, final Profile profile) { - if (playerLocation == null || rangeVar == null) { - return false; - } - - final Location targetLocation; - try { - targetLocation = playerLocation.getLocation(profile); - } catch (final org.betonquest.betonquest.exceptions.QuestRuntimeException e) { - AdventureUtils.consoleMessage(e.getMessage()); - return true; - } - final int range = rangeVar.getInt(profile); - final Location playerLoc = event.getEntity().getLocation(); - return !playerLoc.getWorld().equals(targetLocation.getWorld()) || targetLocation.distanceSquared(playerLoc) > range * range; - } - - @Override - public void start() { - Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance()); - } - - @Override - public void stop() { - HandlerList.unregisterAll(this); - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/quest/ClueScrollCCQuest.java b/plugin/src/main/java/net/momirealms/customcrops/integration/quest/ClueScrollCCQuest.java deleted file mode 100644 index 856be47..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/quest/ClueScrollCCQuest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.integration.quest; - -import com.electro2560.dev.cluescrolls.api.*; -import net.momirealms.customcrops.api.event.CropBreakEvent; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.plugin.Plugin; - -public class ClueScrollCCQuest implements Listener { - - private final CustomClue commonClue; - - public ClueScrollCCQuest(Plugin plugin) { - commonClue = ClueScrollsAPI.getInstance().registerCustomClue(plugin, "harvest", new ClueConfigData("crop_id", DataType.STRING)); - } - - @EventHandler - public void onHarvest(CropBreakEvent event) { - if (event.isCancelled()) return; - if (event.getEntity() instanceof Player player) { - commonClue.handle(player, 1, new ClueDataPair("crop_id", event.getCropItemID())); - } - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/season/CustomCropsSeasonImpl.java b/plugin/src/main/java/net/momirealms/customcrops/integration/season/CustomCropsSeasonImpl.java deleted file mode 100644 index 2cd15bd..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/season/CustomCropsSeasonImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.integration.season; - -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.integration.SeasonInterface; - -public class CustomCropsSeasonImpl implements SeasonInterface { - - @Override - public CCSeason getSeason(String world) { - return CustomCrops.getInstance().getSeasonManager().getSeason(world); - } - - @Override - public int getDate(String world) { - return CustomCrops.getInstance().getSeasonManager().getDate(world); - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/helper/MavenLibraries.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ClassPathAppender.java similarity index 74% rename from plugin/src/main/java/net/momirealms/customcrops/helper/MavenLibraries.java rename to plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ClassPathAppender.java index 5b19c5f..d63541f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/helper/MavenLibraries.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ClassPathAppender.java @@ -1,5 +1,5 @@ /* - * This file is part of helper, licensed under the MIT License. + * This file is part of LuckPerms, licensed under the MIT License. * * Copyright (c) lucko (Luck) * Copyright (c) contributors @@ -23,21 +23,19 @@ * SOFTWARE. */ -package net.momirealms.customcrops.helper; +package net.momirealms.customcrops.libraries.classpath; -import org.jetbrains.annotations.NotNull; - -import java.lang.annotation.*; +import java.nio.file.Path; /** - * Annotation to indicate the required libraries for a class. + * Interface which allows access to add URLs to the plugin classpath at runtime. */ -@Documented -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface MavenLibraries { +public interface ClassPathAppender extends AutoCloseable { - @NotNull - MavenLibrary[] value() default {}; + void addJarToClasspath(Path file); + @Override + default void close() { + + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/JarInJarClassPathAppender.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/JarInJarClassPathAppender.java new file mode 100644 index 0000000..13b2d40 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/JarInJarClassPathAppender.java @@ -0,0 +1,62 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.classpath; + +import net.momirealms.customcrops.libraries.loader.JarInJarClassLoader; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.file.Path; + +public class JarInJarClassPathAppender implements ClassPathAppender { + private final JarInJarClassLoader classLoader; + + public JarInJarClassPathAppender(ClassLoader classLoader) { + if (!(classLoader instanceof JarInJarClassLoader)) { + throw new IllegalArgumentException("Loader is not a JarInJarClassLoader: " + classLoader.getClass().getName()); + } + this.classLoader = (JarInJarClassLoader) classLoader; + } + + @Override + public void addJarToClasspath(Path file) { + try { + this.classLoader.addJarToClasspath(file.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void close() { + this.classLoader.deleteJarResource(); + try { + this.classLoader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ReflectionClassPathAppender.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ReflectionClassPathAppender.java new file mode 100644 index 0000000..101c50e --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/ReflectionClassPathAppender.java @@ -0,0 +1,52 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.classpath; + +import java.net.MalformedURLException; +import java.net.URLClassLoader; +import java.nio.file.Path; + +public class ReflectionClassPathAppender implements ClassPathAppender { + private final URLClassLoaderAccess classLoaderAccess; + + public ReflectionClassPathAppender(ClassLoader classLoader) throws IllegalStateException { + if (classLoader instanceof URLClassLoader) { + this.classLoaderAccess = URLClassLoaderAccess.create((URLClassLoader) classLoader); + } else { + throw new IllegalStateException("ClassLoader is not instance of URLClassLoader"); + } + } + + @Override + public void addJarToClasspath(Path file) { + try { + this.classLoaderAccess.addURL(file.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/helper/URLClassLoaderAccess.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/URLClassLoaderAccess.java similarity index 62% rename from plugin/src/main/java/net/momirealms/customcrops/helper/URLClassLoaderAccess.java rename to plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/URLClassLoaderAccess.java index 0d7a2cc..1a151d4 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/helper/URLClassLoaderAccess.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/classpath/URLClassLoaderAccess.java @@ -1,5 +1,5 @@ /* - * This file is part of helper, licensed under the MIT License. + * This file is part of LuckPerms, licensed under the MIT License. * * Copyright (c) lucko (Luck) * Copyright (c) contributors @@ -23,11 +23,12 @@ * SOFTWARE. */ -package net.momirealms.customcrops.helper; +package net.momirealms.customcrops.libraries.classpath; -import org.jetbrains.annotations.NotNull; +import org.checkerframework.checker.nullness.qual.NonNull; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.Collection; @@ -43,8 +44,10 @@ public abstract class URLClassLoaderAccess { * @param classLoader the class loader * @return the access object */ - static URLClassLoaderAccess create(URLClassLoader classLoader) { - if (Unsafe.isSupported()) { + public static URLClassLoaderAccess create(URLClassLoader classLoader) { + if (Reflection.isSupported()) { + return new Reflection(classLoader); + } else if (Unsafe.isSupported()) { return new Unsafe(classLoader); } else { return Noop.INSTANCE; @@ -63,7 +66,48 @@ public abstract class URLClassLoaderAccess { * * @param url the URL to add */ - public abstract void addURL(@NotNull URL url); + public abstract void addURL(@NonNull URL url); + + private static void throwError(Throwable cause) throws UnsupportedOperationException { + throw new UnsupportedOperationException("CustomCrops is unable to inject into the plugin URLClassLoader.\n" + + "You may be able to fix this problem by adding the following command-line argument " + + "directly after the 'java' command in your start script: \n'--add-opens java.base/java.lang=ALL-UNNAMED'", cause); + } + + /** + * Accesses using reflection, not supported on Java 9+. + */ + private static class Reflection extends URLClassLoaderAccess { + private static final Method ADD_URL_METHOD; + + static { + Method addUrlMethod; + try { + addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + addUrlMethod.setAccessible(true); + } catch (Exception e) { + addUrlMethod = null; + } + ADD_URL_METHOD = addUrlMethod; + } + + private static boolean isSupported() { + return ADD_URL_METHOD != null; + } + + Reflection(URLClassLoader classLoader) { + super(classLoader); + } + + @Override + public void addURL(@NonNull URL url) { + try { + ADD_URL_METHOD.invoke(super.classLoader, url); + } catch (ReflectiveOperationException e) { + URLClassLoaderAccess.throwError(e); + } + } + } /** * Accesses using sun.misc.Unsafe, supported on Java 9+. @@ -106,6 +150,7 @@ public abstract class URLClassLoaderAccess { unopenedURLs = null; pathURLs = null; } + this.unopenedURLs = unopenedURLs; this.pathURLs = pathURLs; } @@ -117,9 +162,15 @@ public abstract class URLClassLoaderAccess { } @Override - public void addURL(@NotNull URL url) { - this.unopenedURLs.add(url); - this.pathURLs.add(url); + public void addURL(@NonNull URL url) { + if (this.unopenedURLs == null || this.pathURLs == null) { + URLClassLoaderAccess.throwError(new NullPointerException("unopenedURLs or pathURLs")); + } + + synchronized (this.unopenedURLs) { + this.unopenedURLs.add(url); + this.pathURLs.add(url); + } } } @@ -131,8 +182,9 @@ public abstract class URLClassLoaderAccess { } @Override - public void addURL(@NotNull URL url) { - throw new UnsupportedOperationException(); + public void addURL(@NonNull URL url) { + URLClassLoaderAccess.throwError(null); } } + } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java new file mode 100644 index 0000000..3618da9 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/Dependency.java @@ -0,0 +1,308 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.dependencies; + +import com.google.common.collect.ImmutableList; +import net.momirealms.customcrops.libraries.dependencies.relocation.Relocation; +import org.jetbrains.annotations.Nullable; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Locale; + +/** + * The dependencies used by LuckPerms. + */ +public enum Dependency { + + ASM( + "org.ow2.asm", + "asm", + "9.1", + null, + "asm" + ), + ASM_COMMONS( + "org.ow2.asm", + "asm-commons", + "9.1", + null, + "asm-commons" + ), + JAR_RELOCATOR( + "me.lucko", + "jar-relocator", + "1.7", + null, + "jar-relocator" + ), + ADVENTURE_API( + "net{}kyori", + "adventure-api", + "4.15.0", + null, + "adventure-api", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_KEY( + "net{}kyori", + "adventure-key", + "4.15.0", + null, + "adventure-key", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_NBT( + "net{}kyori", + "adventure-nbt", + "4.15.0", + null, + "adventure-nbt", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_LEGACY_SERIALIZER( + "net{}kyori", + "adventure-text-serializer-legacy", + "4.15.0", + null, + "adventure-text-serializer-legacy", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_TEXT_LOGGER( + "net{}kyori", + "adventure-text-logger-slf4j", + "4.15.0", + null, + "adventure-text-logger-slf4j", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_GSON( + "net{}kyori", + "adventure-text-serializer-gson", + "4.15.0", + null, + "adventure-text-serializer-gson", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_GSON_LEGACY( + "net{}kyori", + "adventure-text-serializer-gson-legacy-impl", + "4.15.0", + null, + "adventure-text-serializer-gson-legacy-impl", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_PLATFORM( + "net{}kyori", + "adventure-platform-api", + "4.3.2", + null, + "adventure-platform-api", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_PLATFORM_BUKKIT( + "net{}kyori", + "adventure-platform-bukkit", + "4.3.2", + null, + "adventure-platform-bukkit", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_PLATFORM_FACET( + "net{}kyori", + "adventure-platform-facet", + "4.3.2", + null, + "adventure-platform-facet", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + ADVENTURE_TEXT_MINIMESSAGE( + "net{}kyori", + "adventure-text-minimessage", + "4.15.0", + null, + "adventure-text-minimessage", + Relocation.of("adventure", "net{}kyori{}adventure") + ), + COMMAND_API( + "dev{}jorel", + "commandapi-bukkit-shade", + "9.3.0", + null, + "commandapi-bukkit", + Relocation.of("commandapi", "dev{}jorel{}commandapi") + ), + BOOSTED_YAML( + "dev{}dejvokep", + "boosted-yaml", + "1.3.2", + null, + "boosted-yaml", + Relocation.of("boostedyaml", "dev{}dejvokep{}boostedyaml") + ), + H2_DRIVER( + "com.h2database", + "h2", + "2.2.224", + null, + "h2database" + ), + SQLITE_DRIVER( + "org.xerial", + "sqlite-jdbc", + "3.45.1.0", + null, + "sqlite-jdbc" + ), + SLF4J_SIMPLE( + "org.slf4j", + "slf4j-simple", + "2.0.12", + null, + "slf4j-simple" + ), + SLF4J_API( + "org.slf4j", + "slf4j-api", + "2.0.12", + null, + "slf4j-api" + ), + NBT_API( + "de{}tr7zw", + "item-nbt-api", + "2.12.2", + "codemc", + "item-nbt-api", + Relocation.of("changeme", "de{}tr7zw{}changeme") + ), + ANTI_GRIEF( + "com{}github{}Xiao-MoMi", + "AntiGriefLib", + "0.7", + "jitpack", + "antigrief-lib", + Relocation.of("antigrieflib", "net{}momirealms{}antigrieflib") + ), + BIOME_API( + "com{}github{}Xiao-MoMi", + "BiomeAPI", + "0.2", + "jitpack", + "biome-api", + Relocation.of("biomeapi", "net{}momirealms{}biomeapi") + ), + BSTATS_BASE( + "org{}bstats", + "bstats-base", + "3.0.2", + null, + "bstats-base", + Relocation.of("bstats", "org{}bstats") + ), + BSTATS_BUKKIT( + "org{}bstats", + "bstats-bukkit", + "3.0.2", + null, + "bstats-bukkit", + Relocation.of("bstats", "org{}bstats") + ), + GSON( + "com.google.code.gson", + "gson", + "2.10.1", + null, + "gson" + ); + + private final String mavenRepoPath; + private final String version; + private final List relocations; + private final String repo; + private final String artifact; + + private static final String MAVEN_FORMAT = "%s/%s/%s/%s-%s.jar"; + + Dependency(String groupId, String artifactId, String version, String repo, String artifact) { + this(groupId, artifactId, version, repo, artifact, new Relocation[0]); + } + + Dependency(String groupId, String artifactId, String version, String repo, String artifact, Relocation... relocations) { + this.mavenRepoPath = String.format(MAVEN_FORMAT, + rewriteEscaping(groupId).replace(".", "/"), + rewriteEscaping(artifactId), + version, + rewriteEscaping(artifactId), + version + ); + this.version = version; + this.relocations = ImmutableList.copyOf(relocations); + this.repo = repo; + this.artifact = artifact; + } + + private static String rewriteEscaping(String s) { + return s.replace("{}", "."); + } + + public String getFileName(String classifier) { + String name = artifact.toLowerCase(Locale.ROOT).replace('_', '-'); + String extra = classifier == null || classifier.isEmpty() + ? "" + : "-" + classifier; + + return name + "-" + this.version + extra + ".jar"; + } + + String getMavenRepoPath() { + return this.mavenRepoPath; + } + + public List getRelocations() { + return this.relocations; + } + + /** + * Creates a {@link MessageDigest} suitable for computing the checksums + * of dependencies. + * + * @return the digest + */ + public static MessageDigest createDigest() { + try { + return MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + @Nullable + public String getRepo() { + return repo; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/helper/Log.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyDownloadException.java similarity index 53% rename from plugin/src/main/java/net/momirealms/customcrops/helper/Log.java rename to plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyDownloadException.java index faea565..515e589 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/helper/Log.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyDownloadException.java @@ -1,5 +1,5 @@ /* - * This file is part of helper, licensed under the MIT License. + * This file is part of LuckPerms, licensed under the MIT License. * * Copyright (c) lucko (Luck) * Copyright (c) contributors @@ -23,40 +23,26 @@ * SOFTWARE. */ -package net.momirealms.customcrops.helper; - -import net.momirealms.customcrops.CustomCrops; -import org.bukkit.Bukkit; -import org.jetbrains.annotations.NotNull; - -import java.util.logging.Level; +package net.momirealms.customcrops.libraries.dependencies; /** - * Utility for quickly accessing a logger instance without using {@link Bukkit#getLogger()} + * Exception thrown if a dependency cannot be downloaded. */ -public final class Log { +public class DependencyDownloadException extends Exception { + + public DependencyDownloadException() { - public static void info(@NotNull String s) { - CustomCrops.getInstance().getLogger().info(s); } - public static void warn(@NotNull String s) { - CustomCrops.getInstance().getLogger().warning(s); + public DependencyDownloadException(String message) { + super(message); } - public static void severe(@NotNull String s) { - CustomCrops.getInstance().getLogger().severe(s); + public DependencyDownloadException(String message, Throwable cause) { + super(message, cause); } - public static void warn(@NotNull String s, Throwable t) { - CustomCrops.getInstance().getLogger().log(Level.WARNING, s, t); - } - - public static void severe(@NotNull String s, Throwable t) { - CustomCrops.getInstance().getLogger().log(Level.SEVERE, s, t); - } - - private Log() { - throw new UnsupportedOperationException("This class cannot be instantiated"); + public DependencyDownloadException(Throwable cause) { + super(cause); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/helper/MavenLibrary.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManager.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/helper/MavenLibrary.java rename to plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManager.java index 360ae3c..93c7a77 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/helper/MavenLibrary.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManager.java @@ -1,5 +1,5 @@ /* - * This file is part of helper, licensed under the MIT License. + * This file is part of LuckPerms, licensed under the MIT License. * * Copyright (c) lucko (Luck) * Copyright (c) contributors @@ -23,51 +23,31 @@ * SOFTWARE. */ -package net.momirealms.customcrops.helper; +package net.momirealms.customcrops.libraries.dependencies; -import org.jetbrains.annotations.NotNull; - -import java.lang.annotation.*; +import java.util.Collection; +import java.util.Set; /** - * Annotation to indicate a required library for a class. + * Loads and manages runtime dependencies for the plugin. */ -@Documented -@Repeatable(MavenLibraries.class) -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface MavenLibrary { +public interface DependencyManager extends AutoCloseable { /** - * The group id of the library + * Loads dependencies. * - * @return the group id of the library + * @param dependencies the dependencies to load */ - @NotNull - String groupId(); + void loadDependencies(Collection dependencies); /** - * The artifact id of the library + * Obtains an isolated classloader containing the given dependencies. * - * @return the artifact id of the library + * @param dependencies the dependencies + * @return the classloader */ - @NotNull - String artifactId(); - - /** - * The version of the library - * - * @return the version of the library - */ - @NotNull - String version(); - - /** - * The repo where the library can be obtained from - * - * @return the repo where the library can be obtained from - */ - @NotNull - Repository repo() default @Repository(url = "https://repo1.maven.org/maven2"); + ClassLoader obtainClassLoaderWith(Set dependencies); + @Override + void close(); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java new file mode 100644 index 0000000..e24f51a --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyManagerImpl.java @@ -0,0 +1,231 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.dependencies; + +import com.google.common.collect.ImmutableSet; +import net.momirealms.customcrops.CustomCropsPluginImpl; +import net.momirealms.customcrops.libraries.classpath.ClassPathAppender; +import net.momirealms.customcrops.libraries.dependencies.classloader.IsolatedClassLoader; +import net.momirealms.customcrops.libraries.dependencies.relocation.Relocation; +import net.momirealms.customcrops.libraries.dependencies.relocation.RelocationHandler; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CountDownLatch; + +/** + * Loads and manages runtime dependencies for the plugin. + */ +public class DependencyManagerImpl implements DependencyManager { + + /** A registry containing plugin specific behaviour for dependencies. */ + private final DependencyRegistry registry; + /** The path where library jars are cached. */ + private final Path cacheDirectory; + /** The classpath appender to preload dependencies into */ + private final ClassPathAppender classPathAppender; + /** A map of dependencies which have already been loaded. */ + private final EnumMap loaded = new EnumMap<>(Dependency.class); + /** A map of isolated classloaders which have been created. */ + private final Map, IsolatedClassLoader> loaders = new HashMap<>(); + /** Cached relocation handler instance. */ + private @MonotonicNonNull RelocationHandler relocationHandler = null; + + public DependencyManagerImpl(CustomCropsPluginImpl plugin, ClassPathAppender classPathAppender) { + this.registry = new DependencyRegistry(); + this.cacheDirectory = setupCacheDirectory(plugin); + this.classPathAppender = classPathAppender; + } + + private synchronized RelocationHandler getRelocationHandler() { + if (this.relocationHandler == null) { + this.relocationHandler = new RelocationHandler(this); + } + return this.relocationHandler; + } + + @Override + public ClassLoader obtainClassLoaderWith(Set dependencies) { + ImmutableSet set = ImmutableSet.copyOf(dependencies); + + for (Dependency dependency : dependencies) { + if (!this.loaded.containsKey(dependency)) { + throw new IllegalStateException("Dependency " + dependency + " is not loaded."); + } + } + + synchronized (this.loaders) { + IsolatedClassLoader classLoader = this.loaders.get(set); + if (classLoader != null) { + return classLoader; + } + + URL[] urls = set.stream() + .map(this.loaded::get) + .map(file -> { + try { + return file.toUri().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + }) + .toArray(URL[]::new); + + classLoader = new IsolatedClassLoader(urls); + this.loaders.put(set, classLoader); + return classLoader; + } + } + + @Override + public void loadDependencies(Collection dependencies) { + CountDownLatch latch = new CountDownLatch(dependencies.size()); + + for (Dependency dependency : dependencies) { + if (this.loaded.containsKey(dependency)) { + latch.countDown(); + continue; + } + + try { + loadDependency(dependency); + } catch (Throwable e) { + new RuntimeException("Unable to load dependency " + dependency.name(), e).printStackTrace(); + } finally { + latch.countDown(); + } + } + + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private void loadDependency(Dependency dependency) throws Exception { + if (this.loaded.containsKey(dependency)) { + return; + } + + Path file = remapDependency(dependency, downloadDependency(dependency)); + + this.loaded.put(dependency, file); + + if (this.classPathAppender != null && this.registry.shouldAutoLoad(dependency)) { + this.classPathAppender.addJarToClasspath(file); + } + } + + private Path downloadDependency(Dependency dependency) throws DependencyDownloadException { + Path file = this.cacheDirectory.resolve(dependency.getFileName(null)); + + // if the file already exists, don't attempt to re-download it. + if (Files.exists(file)) { + return file; + } + + DependencyDownloadException lastError = null; + + String forceRepo = dependency.getRepo(); + if (forceRepo == null) { + // attempt to download the dependency from each repo in order. + for (DependencyRepository repo : DependencyRepository.values()) { + if (repo.getId().equals("maven") && TimeZone.getDefault().getID().startsWith("Asia")) { + continue; + } + try { + repo.download(dependency, file); + return file; + } catch (DependencyDownloadException e) { + lastError = e; + } + } + } else { + DependencyRepository repository = DependencyRepository.getByID(forceRepo); + if (repository != null) { + try { + repository.download(dependency, file); + return file; + } catch (DependencyDownloadException e) { + lastError = e; + } + } + } + throw Objects.requireNonNull(lastError); + } + + private Path remapDependency(Dependency dependency, Path normalFile) throws Exception { + List rules = new ArrayList<>(dependency.getRelocations()); + if (rules.isEmpty()) { + return normalFile; + } + + Path remappedFile = this.cacheDirectory.resolve(dependency.getFileName(DependencyRegistry.isGsonRelocated() ? "remapped-legacy" : "remapped")); + + // if the remapped source exists already, just use that. + if (Files.exists(remappedFile)) { + return remappedFile; + } + + getRelocationHandler().remap(normalFile, remappedFile, rules); + return remappedFile; + } + + private static Path setupCacheDirectory(CustomCropsPluginImpl plugin) { + File folder = new File(plugin.getDataFolder(), "libs"); + folder.mkdirs(); + return folder.toPath(); + } + + @Override + public void close() { + IOException firstEx = null; + + for (IsolatedClassLoader loader : this.loaders.values()) { + try { + loader.close(); + } catch (IOException ex) { + if (firstEx == null) { + firstEx = ex; + } else { + firstEx.addSuppressed(ex); + } + } + } + + if (firstEx != null) { + firstEx.printStackTrace(); + } + } + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java new file mode 100644 index 0000000..2a0c543 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRegistry.java @@ -0,0 +1,62 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.dependencies; + +import com.google.gson.JsonElement; + +/** + * Applies LuckPerms specific behaviour for {@link Dependency}s. + */ +public class DependencyRegistry { + + public boolean shouldAutoLoad(Dependency dependency) { + return switch (dependency) { + // all used within 'isolated' classloaders, and are therefore not + // relocated. + case ASM, ASM_COMMONS, JAR_RELOCATOR, H2_DRIVER, SQLITE_DRIVER -> false; + default -> true; + }; + } + + @SuppressWarnings("ConstantConditions") + public static boolean isGsonRelocated() { + return JsonElement.class.getName().startsWith("net.momirealms"); + } + + private static boolean classExists(String className) { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + private static boolean slf4jPresent() { + return classExists("org.slf4j.Logger") && classExists("org.slf4j.LoggerFactory"); + } + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java new file mode 100644 index 0000000..53b2e01 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/DependencyRepository.java @@ -0,0 +1,147 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.dependencies; + +import com.google.common.io.ByteStreams; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +/** + * Represents a repository which contains {@link Dependency}s. + */ +public enum DependencyRepository { + + /** + * Maven Central + */ + MAVEN_CENTRAL("maven", "https://repo1.maven.org/maven2/") { + @Override + protected URLConnection openConnection(Dependency dependency) throws IOException { + URLConnection connection = super.openConnection(dependency); + connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(5)); + connection.setReadTimeout((int) TimeUnit.SECONDS.toMillis(5)); + return connection; + } + }, + /** + * Maven Central Mirror + */ + MAVEN_CENTRAL_MIRROR("aliyun", "https://maven.aliyun.com/repository/public/"), + /** + * Code MC + */ + CODE_MC("codemc", "https://repo.codemc.io/repository/maven-public/"), + /** + * Jitpack + */ + JITPACK("jitpack", "https://jitpack.io/"); + + private final String url; + private final String id; + + DependencyRepository(String id, String url) { + this.url = url; + this.id = id; + } + + public static DependencyRepository getByID(String id) { + for (DependencyRepository repository : values()) { + if (id.equals(repository.id)) { + return repository; + } + } + return null; + } + + /** + * Opens a connection to the given {@code dependency}. + * + * @param dependency the dependency to download + * @return the connection + * @throws IOException if unable to open a connection + */ + protected URLConnection openConnection(Dependency dependency) throws IOException { + URL dependencyUrl = new URL(this.url + dependency.getMavenRepoPath()); + return dependencyUrl.openConnection(); + } + + /** + * Downloads the raw bytes of the {@code dependency}. + * + * @param dependency the dependency to download + * @return the downloaded bytes + * @throws DependencyDownloadException if unable to download + */ + public byte[] downloadRaw(Dependency dependency) throws DependencyDownloadException { + try { + URLConnection connection = openConnection(dependency); + try (InputStream in = connection.getInputStream()) { + byte[] bytes = ByteStreams.toByteArray(in); + if (bytes.length == 0) { + throw new DependencyDownloadException("Empty stream"); + } + return bytes; + } + } catch (Exception e) { + throw new DependencyDownloadException(e); + } + } + + /** + * @param dependency the dependency to download + * @return the downloaded bytes + * @throws DependencyDownloadException if unable to download + */ + public byte[] download(Dependency dependency) throws DependencyDownloadException { + return downloadRaw(dependency); + } + + /** + * Downloads the the {@code dependency} to the {@code file}, ensuring the + * downloaded bytes match the checksum. + * + * @param dependency the dependency to download + * @param file the file to write to + * @throws DependencyDownloadException if unable to download + */ + public void download(Dependency dependency, Path file) throws DependencyDownloadException { + try { + Files.write(file, download(dependency)); + } catch (IOException e) { + throw new DependencyDownloadException(e); + } + } + + public String getId() { + return id; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java new file mode 100644 index 0000000..4538d6d --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/classloader/IsolatedClassLoader.java @@ -0,0 +1,54 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.dependencies.classloader; + +import java.net.URL; +import java.net.URLClassLoader; + +/** + * A classloader "isolated" from the rest of the Minecraft server. + * + *

Used to load specific LuckPerms dependencies without causing conflicts + * with other plugins, or libraries provided by the server implementation.

+ */ +public class IsolatedClassLoader extends URLClassLoader { + static { + ClassLoader.registerAsParallelCapable(); + } + + public IsolatedClassLoader(URL[] urls) { + /* + * ClassLoader#getSystemClassLoader returns the AppClassLoader + * + * Calling #getParent on this returns the ExtClassLoader (Java 8) or + * the PlatformClassLoader (Java 9). Since we want this classloader to + * be isolated from the Minecraft server (the app), we set the parent + * to be the platform class loader. + */ + super(urls, ClassLoader.getSystemClassLoader().getParent()); + } + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/Relocation.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/Relocation.java new file mode 100644 index 0000000..3578cc0 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/Relocation.java @@ -0,0 +1,66 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.dependencies.relocation; + +import java.util.Objects; + +public final class Relocation { + private static final String RELOCATION_PREFIX = "net.momirealms.customcrops.libraries."; + + public static Relocation of(String id, String pattern) { + return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id); + } + + private final String pattern; + private final String relocatedPattern; + + private Relocation(String pattern, String relocatedPattern) { + this.pattern = pattern; + this.relocatedPattern = relocatedPattern; + } + + public String getPattern() { + return this.pattern; + } + + public String getRelocatedPattern() { + return this.relocatedPattern; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Relocation that = (Relocation) o; + return Objects.equals(this.pattern, that.pattern) && + Objects.equals(this.relocatedPattern, that.relocatedPattern); + } + + @Override + public int hashCode() { + return Objects.hash(this.pattern, this.relocatedPattern); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHandler.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHandler.java new file mode 100644 index 0000000..2320e85 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHandler.java @@ -0,0 +1,91 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.dependencies.relocation; + +import net.momirealms.customcrops.libraries.dependencies.Dependency; +import net.momirealms.customcrops.libraries.dependencies.DependencyManager; +import net.momirealms.customcrops.libraries.dependencies.classloader.IsolatedClassLoader; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.*; + +/** + * Handles class runtime relocation of packages in downloaded dependencies + */ +public class RelocationHandler { + public static final Set DEPENDENCIES = EnumSet.of(Dependency.ASM, Dependency.ASM_COMMONS, Dependency.JAR_RELOCATOR); + private static final String JAR_RELOCATOR_CLASS = "me.lucko.jarrelocator.JarRelocator"; + private static final String JAR_RELOCATOR_RUN_METHOD = "run"; + + private final Constructor jarRelocatorConstructor; + private final Method jarRelocatorRunMethod; + + public RelocationHandler(DependencyManager dependencyManager) { + ClassLoader classLoader = null; + try { + // download the required dependencies for remapping + dependencyManager.loadDependencies(DEPENDENCIES); + // get a classloader containing the required dependencies as sources + classLoader = dependencyManager.obtainClassLoaderWith(DEPENDENCIES); + + // load the relocator class + Class jarRelocatorClass = classLoader.loadClass(JAR_RELOCATOR_CLASS); + + // prepare the the reflected constructor & method instances + this.jarRelocatorConstructor = jarRelocatorClass.getDeclaredConstructor(File.class, File.class, Map.class); + this.jarRelocatorConstructor.setAccessible(true); + + this.jarRelocatorRunMethod = jarRelocatorClass.getDeclaredMethod(JAR_RELOCATOR_RUN_METHOD); + this.jarRelocatorRunMethod.setAccessible(true); + } catch (Exception e) { + try { + if (classLoader instanceof IsolatedClassLoader) { + ((IsolatedClassLoader) classLoader).close(); + } + } catch (IOException ex) { + e.addSuppressed(ex); + } + + throw new RuntimeException(e); + } + } + + public void remap(Path input, Path output, List relocations) throws Exception { + Map mappings = new HashMap<>(); + for (Relocation relocation : relocations) { + mappings.put(relocation.getPattern(), relocation.getRelocatedPattern()); + } + + // create and invoke a new relocator + Object relocator = this.jarRelocatorConstructor.newInstance(input.toFile(), output.toFile(), mappings); + this.jarRelocatorRunMethod.invoke(relocator); + } + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHelper.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHelper.java new file mode 100644 index 0000000..c5e64ea --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/dependencies/relocation/RelocationHelper.java @@ -0,0 +1,37 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.dependencies.relocation; + +public final class RelocationHelper { + + // screw maven shade + public static final String OKIO_STRING = String.valueOf(new char[]{'o', 'k', 'i', 'o'}); + public static final String OKHTTP3_STRING = String.valueOf(new char[]{'o', 'k', 'h', 't', 't', 'p', '3'}); + + private RelocationHelper() { + + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/JarInJarClassLoader.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/JarInJarClassLoader.java new file mode 100644 index 0000000..34b8760 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/JarInJarClassLoader.java @@ -0,0 +1,155 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.loader; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +/** + * Classloader that can load a jar from within another jar file. + * + *

The "loader" jar contains the loading code & public API classes, + * and is class-loaded by the platform.

+ * + *

The inner "plugin" jar contains the plugin itself, and is class-loaded + * by the loading code & this classloader.

+ */ +public class JarInJarClassLoader extends URLClassLoader { + static { + ClassLoader.registerAsParallelCapable(); + } + + /** + * Creates a new jar-in-jar class loader. + * + * @param loaderClassLoader the loader plugin's classloader (setup and created by the platform) + * @param jarResourcePath the path to the jar-in-jar resource within the loader jar + * @throws LoadingException if something unexpectedly bad happens + */ + public JarInJarClassLoader(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException { + super(new URL[]{extractJar(loaderClassLoader, jarResourcePath)}, loaderClassLoader); + } + + public void addJarToClasspath(URL url) { + addURL(url); + } + + public void deleteJarResource() { + URL[] urls = getURLs(); + if (urls.length == 0) { + return; + } + + try { + Path path = Paths.get(urls[0].toURI()); + Files.deleteIfExists(path); + } catch (Exception e) { + // ignore + } + } + + /** + * Creates a new plugin instance. + * + * @param bootstrapClass the name of the bootstrap plugin class + * @param loaderPluginType the type of the loader plugin, the only parameter of the bootstrap + * plugin constructor + * @param loaderPlugin the loader plugin instance + * @param the type of the loader plugin + * @return the instantiated bootstrap plugin + */ + public LoaderBootstrap instantiatePlugin(String bootstrapClass, Class loaderPluginType, T loaderPlugin) throws LoadingException { + Class plugin; + try { + plugin = loadClass(bootstrapClass).asSubclass(LoaderBootstrap.class); + } catch (ReflectiveOperationException e) { + throw new LoadingException("Unable to load bootstrap class", e); + } + + Constructor constructor; + try { + constructor = plugin.getConstructor(loaderPluginType); + } catch (ReflectiveOperationException e) { + throw new LoadingException("Unable to get bootstrap constructor", e); + } + + try { + return constructor.newInstance(loaderPlugin); + } catch (ReflectiveOperationException e) { + throw new LoadingException("Unable to create bootstrap plugin instance", e); + } + } + + /** + * Extracts the "jar-in-jar" from the loader plugin into a temporary file, + * then returns a URL that can be used by the {@link JarInJarClassLoader}. + * + * @param loaderClassLoader the classloader for the "host" loader plugin + * @param jarResourcePath the inner jar resource path + * @return a URL to the extracted file + */ + private static URL extractJar(ClassLoader loaderClassLoader, String jarResourcePath) throws LoadingException { + // get the jar-in-jar resource + URL jarInJar = loaderClassLoader.getResource(jarResourcePath); + if (jarInJar == null) { + throw new LoadingException("Could not locate jar-in-jar"); + } + + // create a temporary file + // on posix systems by default this is only read/writable by the process owner + Path path; + try { + path = Files.createTempFile("customcrops-jarinjar", ".jar.tmp"); + } catch (IOException e) { + throw new LoadingException("Unable to create a temporary file", e); + } + + // mark that the file should be deleted on exit + path.toFile().deleteOnExit(); + + // copy the jar-in-jar to the temporary file path + try (InputStream in = jarInJar.openStream()) { + Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new LoadingException("Unable to copy jar-in-jar to temporary path", e); + } + + try { + return path.toUri().toURL(); + } catch (MalformedURLException e) { + throw new LoadingException("Unable to get URL from path", e); + } + } + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/helper/Repository.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoaderBootstrap.java similarity index 74% rename from plugin/src/main/java/net/momirealms/customcrops/helper/Repository.java rename to plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoaderBootstrap.java index 34e7feb..fbdd07f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/helper/Repository.java +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoaderBootstrap.java @@ -1,5 +1,5 @@ /* - * This file is part of helper, licensed under the MIT License. + * This file is part of LuckPerms, licensed under the MIT License. * * Copyright (c) lucko (Luck) * Copyright (c) contributors @@ -23,23 +23,17 @@ * SOFTWARE. */ -package net.momirealms.customcrops.helper; - -import java.lang.annotation.*; +package net.momirealms.customcrops.libraries.loader; /** - * Represents a maven repository. + * Minimal bootstrap plugin, called by the loader plugin. */ -@Documented -@Target(ElementType.LOCAL_VARIABLE) -@Retention(RetentionPolicy.RUNTIME) -public @interface Repository { +public interface LoaderBootstrap { - /** - * Gets the base url of the repository. - * - * @return the base url of the repository - */ - String url(); + void onLoad(); + + default void onEnable() {} + + default void onDisable() {} } diff --git a/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoadingException.java b/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoadingException.java new file mode 100644 index 0000000..2d5e925 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/libraries/loader/LoadingException.java @@ -0,0 +1,41 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.libraries.loader; + +/** + * Runtime exception used if there is a problem during loading + */ +public class LoadingException extends RuntimeException { + + public LoadingException(String message) { + super(message); + } + + public LoadingException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java new file mode 100644 index 0000000..da2f6f0 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/AdventureManagerImpl.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.manager; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.title.Title; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.AdventureManager; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.MessageManager; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; + +import java.time.Duration; + +public class AdventureManagerImpl extends AdventureManager { + + private final CustomCropsPlugin plugin; + private BukkitAudiences audiences; + + public AdventureManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + init(); + } + + @Override + public void init() { + this.audiences = BukkitAudiences.create(plugin); + } + + @Override + public void disable() { + if (this.audiences != null) + this.audiences.close(); + } + + @Override + public void sendMessage(CommandSender sender, String text) { + if (text == null) return; + if (sender instanceof Player player) sendPlayerMessage(player, text); + else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(text); + } + + + @Override + public void sendMessageWithPrefix(CommandSender sender, String text) { + if (text == null) return; + if (sender instanceof Player player) sendPlayerMessage(player, MessageManager.prefix() + text); + else if (sender instanceof ConsoleCommandSender) sendConsoleMessage(MessageManager.prefix() + text); + } + + @Override + public void sendConsoleMessage(String text) { + if (text == null) return; + Audience au = audiences.sender(Bukkit.getConsoleSender()); + au.sendMessage(getComponentFromMiniMessage(text)); + } + + @Override + public void sendPlayerMessage(Player player, String text) { + if (player == null) return; + Audience au = audiences.player(player); + au.sendMessage(getComponentFromMiniMessage(text)); + } + + @Override + public void sendActionbar(Player player, String text) { + if (player == null) return; + Audience au = audiences.player(player); + au.sendActionBar(getComponentFromMiniMessage(text)); + } + + @Override + public void sendSound(Player player, Sound.Source source, Key key, float pitch, float volume) { + if (player == null) return; + sendSound(player, Sound.sound(key, source, volume, pitch)); + } + + @Override + public void sendSound(Player player, Sound sound) { + if (player == null) return; + Audience au = audiences.player(player); + au.playSound(sound); + } + + @Override + public void sendTitle(Player player, String title, String subTitle, int fadeIn, int stay, int fadeOut) { + if (player == null) return; + Audience au = audiences.player(player); + au.showTitle(Title.title(getComponentFromMiniMessage(title), getComponentFromMiniMessage(subTitle), Title.Times.times( + Duration.ofMillis(fadeIn * 50L), + Duration.ofMillis(stay * 50L), + Duration.ofMillis(fadeOut * 50L) + ))); + } + + @Override + public Component getComponentFromMiniMessage(String text) { + if (text == null) { + return Component.empty(); + } + if (ConfigManager.legacyColorSupport()) { + return MiniMessage.miniMessage().deserialize(legacyToMiniMessage(text)); + } else { + return MiniMessage.miniMessage().deserialize(text); + } + } + + @Override + public String legacyToMiniMessage(String legacy) { + StringBuilder stringBuilder = new StringBuilder(); + char[] chars = legacy.toCharArray(); + for (int i = 0; i < chars.length; i++) { + if (!isColorCode(chars[i])) { + stringBuilder.append(chars[i]); + continue; + } + if (i + 1 >= chars.length) { + stringBuilder.append(chars[i]); + continue; + } + switch (chars[i+1]) { + case '0' -> stringBuilder.append(""); + case '1' -> stringBuilder.append(""); + case '2' -> stringBuilder.append(""); + case '3' -> stringBuilder.append(""); + case '4' -> stringBuilder.append(""); + case '5' -> stringBuilder.append(""); + case '6' -> stringBuilder.append(""); + case '7' -> stringBuilder.append(""); + case '8' -> stringBuilder.append(""); + case '9' -> stringBuilder.append(""); + case 'a' -> stringBuilder.append(""); + case 'b' -> stringBuilder.append(""); + case 'c' -> stringBuilder.append(""); + case 'd' -> stringBuilder.append(""); + case 'e' -> stringBuilder.append(""); + case 'f' -> stringBuilder.append(""); + case 'r' -> stringBuilder.append(""); + case 'l' -> stringBuilder.append(""); + case 'm' -> stringBuilder.append(""); + case 'o' -> stringBuilder.append(""); + case 'n' -> stringBuilder.append(""); + case 'k' -> stringBuilder.append(""); + case 'x' -> { + if (i + 13 >= chars.length + || !isColorCode(chars[i+2]) + || !isColorCode(chars[i+4]) + || !isColorCode(chars[i+6]) + || !isColorCode(chars[i+8]) + || !isColorCode(chars[i+10]) + || !isColorCode(chars[i+12])) { + stringBuilder.append(chars[i]); + continue; + } + stringBuilder + .append("<#") + .append(chars[i+3]) + .append(chars[i+5]) + .append(chars[i+7]) + .append(chars[i+9]) + .append(chars[i+11]) + .append(chars[i+13]) + .append(">"); + i += 12; + } + default -> { + stringBuilder.append(chars[i]); + continue; + } + } + i++; + } + return stringBuilder.toString(); + } + + @Override + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean isColorCode(char c) { + return c == '§' || c == '&'; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java new file mode 100644 index 0000000..1de3420 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/CommandManager.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.manager; + +import dev.jorel.commandapi.*; +import dev.jorel.commandapi.arguments.ArgumentSuggestions; +import dev.jorel.commandapi.arguments.IntegerArgument; +import dev.jorel.commandapi.arguments.StringArgument; +import dev.jorel.commandapi.arguments.WorldArgument; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Initable; +import net.momirealms.customcrops.api.integration.SeasonInterface; +import net.momirealms.customcrops.api.manager.MessageManager; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.compatibility.season.InBuiltSeason; +import org.bukkit.World; + +import java.util.Locale; +import java.util.Optional; + +public class CommandManager implements Initable { + + private final CustomCropsPlugin plugin; + + public CommandManager(CustomCropsPlugin plugin) { + this.plugin = plugin; + init(); + } + + @Override + public void init() { + if (!CommandAPI.isLoaded()) + CommandAPI.onLoad(new CommandAPIBukkitConfig(plugin).silentLogs(true)); + new CommandAPICommand("customcrops") + .withPermission(CommandPermission.OP) + .withAliases("ccrops") + .withSubcommands( + getReloadCommand(), + getAboutCommand(), + getSeasonCommand(), + getDateCommand() + //getStressTest() + ) + .register(); + } + + @Override + public void disable() { + CommandAPI.unregister("customcrops"); + } + + private CommandAPICommand getReloadCommand() { + return new CommandAPICommand("reload") + .executes((sender, args) -> { + long time1 = System.currentTimeMillis(); + plugin.reload(); + long time2 = System.currentTimeMillis(); + plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.reloadMessage().replace("{time}", String.valueOf(time2 - time1))); + }); + } + + private CommandAPICommand getAboutCommand() { + return new CommandAPICommand("about").executes((sender, args) -> { + plugin.getAdventure().sendMessage(sender, "<#FFA500>⛈ CustomCrops - <#87CEEB>" + CustomCropsPlugin.getInstance().getVersionManager().getPluginVersion()); + plugin.getAdventure().sendMessage(sender, "<#FFFFE0>Ultra-customizable planting experience for Minecraft servers"); + plugin.getAdventure().sendMessage(sender, "<#DA70D6>\uD83E\uDDEA Author: <#FFC0CB>XiaoMoMi"); + plugin.getAdventure().sendMessage(sender, "<#FF7F50>\uD83D\uDD25 Contributors: <#FFA07A>Cha_Shao, <#FFA07A>TopOrigin, <#FFA07A>AmazingCat"); + plugin.getAdventure().sendMessage(sender, "<#FFD700>⭐ Document <#A9A9A9>| <#FAFAD2>⛏ Github <#A9A9A9>| <#48D1CC>\uD83D\uDD14 Polymart"); + }); + } + + private CommandAPICommand getDateCommand() { + return new CommandAPICommand("date") + .withSubcommands( + new CommandAPICommand("get") + .withArguments(new WorldArgument("world")) + .executes((sender, args) -> { + World world = (World) args.get("world"); + plugin.getAdventure().sendMessageWithPrefix(sender, String.valueOf(plugin.getIntegrationManager().getDate(world))); + }), + new CommandAPICommand("set") + .withArguments(new WorldArgument("world")) + .withArguments(new IntegerArgument("date",1)) + .executes((sender, args) -> { + World world = (World) args.get("world"); + int date = (int) args.getOrDefault("date", 1); + SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface(); + if (!(seasonInterface instanceof InBuiltSeason inBuiltSeason)) { + plugin.getAdventure().sendMessageWithPrefix(sender, "Detected that you are using a season plugin. Please set date in that plugin."); + return; + } + Optional customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world); + if (customCropsWorld.isEmpty()) { + plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); + return; + } + if (!customCropsWorld.get().getWorldSetting().isEnableSeason()) { + plugin.getAdventure().sendMessageWithPrefix(sender, "Season is not enabled in that world"); + return; + } + if (date > customCropsWorld.get().getWorldSetting().getSeasonDuration()) { + plugin.getAdventure().sendMessageWithPrefix(sender, "Date should be a value no higher than season duration"); + return; + } + String pre = String.valueOf(inBuiltSeason.getDate(world)); + customCropsWorld.get().getInfoData().setDate(date); + plugin.getAdventure().sendMessageWithPrefix(sender, "Date in world("+world.getName()+"): " + pre + " -> " + date); + }) + ); + } + + private CommandAPICommand getSeasonCommand() { + return new CommandAPICommand("season") + .withSubcommands( + new CommandAPICommand("get") + .withArguments(new WorldArgument("world")) + .executes((sender, args) -> { + World world = (World) args.get("world"); + plugin.getAdventure().sendMessageWithPrefix(sender, MessageManager.seasonTranslation(plugin.getIntegrationManager().getSeason(world))); + }), + new CommandAPICommand("set") + .withArguments(new WorldArgument("world")) + .withArguments(new StringArgument("season") + .replaceSuggestions(ArgumentSuggestions.stringsWithTooltips(info -> + new IStringTooltip[] { + StringTooltip.ofString("Spring", MessageManager.seasonTranslation(Season.SPRING)), + StringTooltip.ofString("Summer", MessageManager.seasonTranslation(Season.SUMMER)), + StringTooltip.ofString("Autumn", MessageManager.seasonTranslation(Season.AUTUMN)), + StringTooltip.ofString("Winter", MessageManager.seasonTranslation(Season.WINTER)) + } + )) + ) + .executes((sender, args) -> { + World world = (World) args.get("world"); + String seasonName = (String) args.get("season"); + + SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface(); + if (!(seasonInterface instanceof InBuiltSeason inBuiltSeason)) { + plugin.getAdventure().sendMessageWithPrefix(sender, "Detected that you are using a season plugin. Please set season in that plugin."); + return; + } + String pre = MessageManager.seasonTranslation(inBuiltSeason.getSeason(world)); + Optional customCropsWorld = plugin.getWorldManager().getCustomCropsWorld(world); + if (customCropsWorld.isEmpty()) { + plugin.getAdventure().sendMessageWithPrefix(sender, "CustomCrops is not enabled in that world"); + return; + } + if (!customCropsWorld.get().getWorldSetting().isEnableSeason()) { + plugin.getAdventure().sendMessageWithPrefix(sender, "Season is not enabled in that world"); + return; + } + try { + Season season = Season.valueOf(seasonName.toUpperCase(Locale.ENGLISH)); + customCropsWorld.get().getInfoData().setSeason(season); + String next = MessageManager.seasonTranslation(season); + plugin.getAdventure().sendMessageWithPrefix(sender, "Season in world("+world.getName()+"): " + pre + " -> " + next); + } catch (IllegalArgumentException e) { + plugin.getAdventure().sendMessageWithPrefix(sender, "That season doesn't exist"); + } + }) + ); + } + +// private CommandAPICommand getStressTest() { +// return new CommandAPICommand("test").executes((sender, args) -> { +// for (int i = 0; i < 16; i++) { +// for (int j = 0; j < 16; j++) { +// for (int k = -64; k < 0; k++) { +// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j); +// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "tomato", 0), location); +// } +// for (int k = 1; k < 64; k++) { +// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j); +// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "tomato", 1), location); +// } +// for (int k = 65; k < 128; k++) { +// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j); +// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "tomato", 2), location); +// } +// for (int k = 129; k < 165; k++) { +// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j); +// plugin.getWorldManager().addPotAt(new MemoryPot(location, "default"), location); +// } +// for (int k = 166; k < 190; k++) { +// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j); +// plugin.getWorldManager().addPotAt(new MemoryPot(location, "sprinkler"), location); +// } +// for (int k = 191; k < 250; k++) { +// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j); +// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "tomato", 3), location); +// } +// for (int k = 251; k < 300; k++) { +// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j); +// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "sbsbssbsb", 3), location); +// } +// for (int k = 301; k < 320; k++) { +// SimpleLocation location = new SimpleLocation("world", 1024 + i, k, 1024 + j); +// plugin.getWorldManager().addCropAt(new MemoryCrop(location, "sbsbssbsb", 2), location); +// } +// } +// } +// }); +// } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java new file mode 100644 index 0000000..48aa7bf --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/ConfigManagerImpl.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.manager; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.utils.ConfigUtils; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class ConfigManagerImpl extends ConfigManager { + + private static final String configVersion = "35"; + private CustomCropsPlugin plugin; + private String lang; + private int maximumPoolSize; + private int corePoolSize; + private int keepAliveTime; + private boolean debug; + private boolean metrics; + private boolean legacyColorSupport; + private boolean protectLore; + private String[] itemDetectionOrder; + private boolean checkUpdate; + private boolean disableMoisture; + private boolean preventTrampling; + private boolean greenhouse; + private boolean scarecrow; + private double[] defaultQualityRatio; + private String greenhouseID; + private String scarecrowID; + private int greenhouseRange; + private int scarecrowRange; + private boolean syncSeasons; + private WeakReference referenceWorld; + + public ConfigManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void load() { + YamlConfiguration config = ConfigUtils.getConfig("config.yml"); + + debug = config.getBoolean("debug"); + metrics = config.getBoolean("metrics"); + lang = config.getString("lang"); + checkUpdate = config.getBoolean("update-checker", true); + + ConfigurationSection otherSettings = config.getConfigurationSection("other-settings"); + if (otherSettings == null) { + LogUtils.severe("other-settings section should not be null"); + return; + } + + maximumPoolSize = otherSettings.getInt("thread-pool-settings.maximumPoolSize", 10); + corePoolSize = otherSettings.getInt("thread-pool-settings.corePoolSize", 10); + keepAliveTime = otherSettings.getInt("thread-pool-settings.keepAliveTime", 30); + itemDetectionOrder = otherSettings.getStringList("item-detection-order").toArray(new String[0]); + protectLore = otherSettings.getBoolean("protect-original-lore", false); + legacyColorSupport = otherSettings.getBoolean("legacy-color-code-support", true); + + ConfigurationSection mechanics = config.getConfigurationSection("mechanics"); + if (mechanics == null) { + LogUtils.severe("mechanics section should not be null"); + return; + } + + defaultQualityRatio = ConfigUtils.getQualityRatio(mechanics.getString("default-quality-ratio", "17/2/1")); + disableMoisture = mechanics.getBoolean("vanilla-farmland.disable-moisture-mechanic", false); + preventTrampling = mechanics.getBoolean("vanilla-farmland.prevent-trampling", false); + greenhouse = mechanics.getBoolean("greenhouse.enable", true); + greenhouseID = mechanics.getString("greenhouse.id"); + greenhouseRange = mechanics.getInt("greenhouse.range", 5); + + scarecrow = mechanics.getBoolean("scarecrow.enable", true); + scarecrowID = mechanics.getString("scarecrow.id"); + scarecrowRange = mechanics.getInt("scarecrow.range", 7); + + syncSeasons = mechanics.getBoolean("sync-season.enable", true); + if (syncSeasons) { + referenceWorld = new WeakReference<>(Bukkit.getWorld(Objects.requireNonNull(mechanics.getString("sync-season.reference")))); + } + } + + @Override + public void unload() { + + } + + @Override + public boolean hasLegacyColorSupport() { + return legacyColorSupport; + } + + @Override + public int getMaximumPoolSize() { + return maximumPoolSize; + } + + @Override + public int getCorePoolSize() { + return corePoolSize; + } + + @Override + public int getKeepAliveTime() { + return keepAliveTime; + } + + @Override + protected double[] getDefaultQualityRatio() { + return defaultQualityRatio; + } + + @Override + protected String getLang() { + return lang; + } + + @Override + public boolean getDebugMode() { + return debug; + } + + @Override + public boolean isProtectLore() { + return protectLore; + } + + @Override + public String[] getItemDetectionOrder() { + return itemDetectionOrder; + } + + @Override + public boolean hasMetrics() { + return metrics; + } + + @Override + public boolean hasCheckUpdate() { + return checkUpdate; + } + + @Override + public boolean isDisableMoisture() { + return disableMoisture; + } + + @Override + public boolean isPreventTrampling() { + return preventTrampling; + } + + @Override + public boolean isGreenhouseEnabled() { + return greenhouse; + } + + @Override + public String getGreenhouseID() { + return greenhouseID; + } + + @Override + public int getGreenhouseRange() { + return greenhouseRange; + } + + @Override + public boolean isScarecrowEnabled() { + return scarecrow; + } + + @Override + public String getScarecrowID() { + return scarecrowID; + } + + @Override + public int getScarecrowRange() { + return scarecrowRange; + } + + @Override + public boolean isSyncSeasons() { + return syncSeasons; + } + + @Override + public World getReferenceWorld() { + return referenceWorld.get(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/HologramManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java similarity index 65% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/HologramManager.java rename to plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java index 4f18263..f9edcc8 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/hologram/HologramManager.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/HologramManager.java @@ -15,13 +15,14 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.hologram; +package net.momirealms.customcrops.manager; import net.kyori.adventure.text.Component; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.Function; -import net.momirealms.customcrops.api.object.Tuple; -import net.momirealms.customcrops.util.FakeEntityUtils; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.common.Tuple; +import net.momirealms.customcrops.api.scheduler.CancellableTask; +import net.momirealms.customcrops.utils.FakeEntityUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.EntityType; @@ -36,25 +37,31 @@ import java.util.Map; import java.util.UUID; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; -public class HologramManager extends Function implements Listener { +public class HologramManager implements Listener, Reloadable { private final ConcurrentHashMap hologramMap; - private final CustomCrops plugin; - private ScheduledFuture scheduledFuture; + private final CustomCropsPlugin plugin; + private CancellableTask cacheCheckTask; + private static HologramManager manager; - public HologramManager(CustomCrops plugin) { + public static HologramManager getInstance() { + return manager; + } + + public HologramManager(CustomCropsPlugin plugin) { this.plugin = plugin; this.hologramMap = new ConcurrentHashMap<>(); + manager = this; } @Override public void load() { Bukkit.getPluginManager().registerEvents(this, plugin); - scheduledFuture = plugin.getScheduler().runTaskTimerAsync(() -> { - ArrayList removed = new ArrayList<>(4); + this.cacheCheckTask = plugin.getScheduler().runTaskAsyncTimer(() -> { + ArrayList removed = new ArrayList<>(); long current = System.currentTimeMillis(); for (Map.Entry entry : hologramMap.entrySet()) { Player player = Bukkit.getPlayer(entry.getKey()); @@ -67,7 +74,7 @@ public class HologramManager extends Function implements Listener { for (UUID uuid : removed) { hologramMap.remove(uuid); } - }, 500, 500); + }, 200, 200, TimeUnit.MILLISECONDS); } @Override @@ -79,7 +86,7 @@ public class HologramManager extends Function implements Listener { entry.getValue().removeAll(player); } } - if (scheduledFuture != null) scheduledFuture.cancel(false); + if (cacheCheckTask != null) cacheCheckTask.cancel(); this.hologramMap.clear(); } @@ -88,22 +95,17 @@ public class HologramManager extends Function implements Listener { this.hologramMap.remove(event.getPlayer().getUniqueId()); } - public void showHologram(Player player, Location location, Component component, int millis, Mode mode, TextDisplayMeta textDisplayMeta) { + public void showHologram(Player player, Location location, Component component, int millis) { HologramCache hologramCache = hologramMap.get(player.getUniqueId()); if (hologramCache != null) { - hologramCache.showHologram(player, location, component, millis, mode, textDisplayMeta); + hologramCache.showHologram(player, location, component, millis); } else { hologramCache = new HologramCache(); - hologramCache.showHologram(player, location, component, millis, mode, textDisplayMeta); + hologramCache.showHologram(player, location, component, millis); hologramMap.put(player.getUniqueId(), hologramCache); } } - public enum Mode { - ARMOR_STAND, - TEXT_DISPLAY - } - @SuppressWarnings("unchecked") public static class HologramCache { @@ -130,36 +132,26 @@ public class HologramManager extends Function implements Listener { if (tuple.getRight() < current) { tupleList.remove(tuple); this.tuples = tupleList.toArray(new Tuple[0]); - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getDestroyPacket(tuple.getMid())); + PacketManager.getInstance().send(player, FakeEntityUtils.getDestroyPacket(tuple.getMid())); } } } - public void showHologram(Player player, Location location, Component component, int millis, Mode mode, TextDisplayMeta textDisplayMeta) { + public void showHologram(Player player, Location location, Component component, int millis) { int entity_id = push(location, millis); if (entity_id == 0) { int random = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); tupleList.add(Tuple.of(location, random, System.currentTimeMillis() + millis)); this.tuples = tupleList.toArray(new Tuple[0]); - if (mode == Mode.ARMOR_STAND) { - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getSpawnPacket(random, location, EntityType.ARMOR_STAND)); - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getVanishArmorStandMetaPacket(random, component)); - } else if (mode == Mode.TEXT_DISPLAY) { - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getSpawnPacket(random, location.clone().add(0,1,0), EntityType.TEXT_DISPLAY)); - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getTextDisplayMetaPacket(random, component, textDisplayMeta)); - } + PacketManager.getInstance().send(player, FakeEntityUtils.getSpawnPacket(random, location, EntityType.ARMOR_STAND), FakeEntityUtils.getVanishArmorStandMetaPacket(random, component)); } else { - if (mode == Mode.ARMOR_STAND) { - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getVanishArmorStandMetaPacket(entity_id, component)); - } else if (mode == Mode.TEXT_DISPLAY) { - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getTextDisplayMetaPacket(entity_id, component, textDisplayMeta)); - } + PacketManager.getInstance().send(player, FakeEntityUtils.getVanishArmorStandMetaPacket(entity_id, component)); } } public void removeAll(Player player) { for (Tuple tuple : tuples) { - CustomCrops.getProtocolManager().sendServerPacket(player, FakeEntityUtils.getDestroyPacket(tuple.getMid())); + PacketManager.getInstance().send(player, FakeEntityUtils.getDestroyPacket(tuple.getMid())); } this.tupleList.clear(); this.tuples = null; diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java new file mode 100644 index 0000000..ce26b8a --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/MessageManagerImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.manager; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Reloadable; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.MessageManager; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.utils.ConfigUtils; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; + +public class MessageManagerImpl extends MessageManager implements Reloadable { + + private CustomCropsPlugin plugin; + private String reload; + private String prefix; + private String spring; + private String summer; + private String autumn; + private String winter; + private String noSeason; + + public MessageManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void load() { + YamlConfiguration config = ConfigUtils.getConfig("messages" + File.separator + ConfigManager.lang() + ".yml"); + ConfigurationSection section = config.getConfigurationSection("messages"); + if (section != null) { + prefix = section.getString("prefix", "[CustomCrops] "); + reload = section.getString("reload", "Reloaded! Took {time}ms."); + + spring = section.getString("spring", "Spring"); + summer = section.getString("summer", "Summer"); + autumn = section.getString("autumn", "Autumn"); + winter = section.getString("winter", "Winter"); + noSeason = section.getString("no-season", "Season Disabled"); + } + } + + @Override + public void unload() { + + } + + @Override + public String getSeasonTranslation(Season season) { + if (season == null) return noSeason; + return switch (season) { + case SPRING -> spring; + case SUMMER -> summer; + case AUTUMN -> autumn; + case WINTER -> winter; + }; + } + + @Override + public void reload() { + load(); + } + + @Override + public String getPrefix() { + return prefix; + } + + @Override + protected String getReload() { + return reload; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/PacketManager.java b/plugin/src/main/java/net/momirealms/customcrops/manager/PacketManager.java new file mode 100644 index 0000000..cc6f892 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/PacketManager.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.manager; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PacketManager { + + private static PacketManager instance; + private final ProtocolManager protocolManager; + private final CustomCropsPlugin plugin; + + public PacketManager(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.protocolManager = ProtocolLibrary.getProtocolManager(); + instance = this; + } + + public static PacketManager getInstance() { + return instance; + } + + public void send(Player player, PacketContainer packet) { + this.protocolManager.sendServerPacket(player, packet); + } + + public void send(Player player, PacketContainer... packets) { + if (!player.isOnline()) return; + if (plugin.getVersionManager().isVersionNewerThan1_19_R3()) { + List bundle = new ArrayList<>(Arrays.asList(packets)); + PacketContainer bundlePacket = new PacketContainer(PacketType.Play.Server.BUNDLE); + bundlePacket.getPacketBundles().write(0, bundle); + send(player, bundlePacket); + } else { + for (PacketContainer packet : packets) { + send(player, packet); + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java new file mode 100644 index 0000000..6df2ed1 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/PlaceholderManagerImpl.java @@ -0,0 +1,88 @@ +package net.momirealms.customcrops.manager; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.PlaceholderManager; +import net.momirealms.customcrops.compatibility.papi.CCPapi; +import net.momirealms.customcrops.compatibility.papi.ParseUtils; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +public class PlaceholderManagerImpl extends PlaceholderManager { + + private final HashMap customPlaceholderMap; + private final CustomCropsPlugin plugin; + private final boolean hasPapi; + private CCPapi ccPapi; + + public PlaceholderManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.customPlaceholderMap = new HashMap<>(); + this.hasPapi = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); + if (hasPapi) { + ccPapi = new CCPapi(plugin); + } + } + + @Override + public void load() { + if (ccPapi != null) { + ccPapi.register(); + } + } + + @Override + public void unload() { + if (ccPapi != null) { + ccPapi.unregister(); + } + } + + @Override + public String parse(@Nullable Player player, String text, Map placeholders) { + var list = detectPlaceholders(text); + for (String papi : list) { + String replacer = null; + if (placeholders != null) { + replacer = placeholders.get(papi); + } + if (replacer == null) { + String custom = customPlaceholderMap.get(papi); + if (custom != null) { + replacer = setPlaceholders(player, parse(player, custom, placeholders)); + } + } + if (replacer != null) { + text = text.replace(papi, replacer); + } + } + return text; + } + + @Override + public List parse(@Nullable Player player, List list, Map replacements) { + return list.stream() + .map(s -> parse(player, s, replacements)) + .collect(Collectors.toList()); + } + + @Override + public List detectPlaceholders(String text) { + List placeholders = new ArrayList<>(); + Matcher matcher = pattern.matcher(text); + while (matcher.find()) placeholders.add(matcher.group()); + return placeholders; + } + + @Override + public String setPlaceholders(Player player, String text) { + return hasPapi ? ParseUtils.setPlaceholders(player, text) : text; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/helper/VersionHelper.java b/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java similarity index 53% rename from plugin/src/main/java/net/momirealms/customcrops/helper/VersionHelper.java rename to plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java index 7fe65e0..1762e81 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/helper/VersionHelper.java +++ b/plugin/src/main/java/net/momirealms/customcrops/manager/VersionManagerImpl.java @@ -15,96 +15,122 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.helper; +package net.momirealms.customcrops.manager; -import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion; -import de.tr7zw.changeme.nbtapi.utils.VersionChecker; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.util.AdventureUtils; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.VersionManager; +import net.momirealms.customcrops.api.util.LogUtils; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; -import java.lang.reflect.Field; import java.net.URL; import java.net.URLConnection; +import java.util.concurrent.CompletableFuture; -public class VersionHelper { +public class VersionManagerImpl extends VersionManager { - private boolean isNewerThan1_19_R2; - private boolean isNewerThan1_19_R3; - private String server_version; - private final CustomCrops plugin; + private final CustomCropsPlugin plugin; + private final String pluginVersion; + private final String serverVersion; + private boolean hasRegionScheduler; private final boolean isSpigot; + private final boolean isNewerThan1_19_R2; + private final boolean isNewerThan1_19_R3; + private final boolean isNewerThan1_20; + private final boolean isNewerThan1_19; + private boolean isMojmap; - public VersionHelper(CustomCrops plugin) { + @SuppressWarnings("deprecation") + public VersionManagerImpl(CustomCropsPlugin plugin) { this.plugin = plugin; - this.initialize(); - this.disableUseLessInfo(); this.isSpigot = plugin.getServer().getName().equals("CraftBukkit"); - } + this.pluginVersion = plugin.getDescription().getVersion(); + this.serverVersion = plugin.getServer().getClass().getPackage().getName().split("\\.")[3]; + String[] split = serverVersion.split("_"); + int main_ver = Integer.parseInt(split[1]); + + if (main_ver >= 20) { + isNewerThan1_19_R2 = true; + isNewerThan1_19_R3 = true; + isNewerThan1_20 = true; + isNewerThan1_19 = true; + } else if (main_ver == 19) { + isNewerThan1_19_R2 = Integer.parseInt(split[2].substring(1)) >= 2; + isNewerThan1_19_R3 = Integer.parseInt(split[2].substring(1)) >= 3; + isNewerThan1_20 = false; + isNewerThan1_19 = true; + } else { + isNewerThan1_19_R2 = false; + isNewerThan1_19_R3 = false; + isNewerThan1_20 = false; + isNewerThan1_19 = false; + } + + try { + Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler"); + this.hasRegionScheduler = true; + } catch (ClassNotFoundException ignored) { + this.hasRegionScheduler = false; + } + + // Check if the server is Mojmap + try { + Class.forName("net.minecraft.network.protocol.game.ClientboundBossEventPacket"); + this.isMojmap = true; + } catch (ClassNotFoundException ignored) { - public boolean isVersionNewerThan1_19_R2() { - if (server_version == null) { - initialize(); } - return isNewerThan1_19_R2; } + @Override + public boolean hasRegionScheduler() { + return hasRegionScheduler; + } + + @Override + public String getPluginVersion() { + return pluginVersion; + } + + @Override + public String getServerVersion() { + return serverVersion; + } + + @Override + public boolean isSpigot() { + return isSpigot; + } + + @Override public boolean isVersionNewerThan1_19_R3() { - if (server_version == null) { - initialize(); - } return isNewerThan1_19_R3; } - private void initialize() { - server_version = plugin.getServer().getClass().getPackage().getName().split("\\.")[3]; - String[] split = server_version.split("_"); - int main_ver = Integer.parseInt(split[1]); - if (main_ver >= 20) isNewerThan1_19_R2 = (isNewerThan1_19_R3 = true); - else if (main_ver == 19) { - isNewerThan1_19_R2 = Integer.parseInt(split[2].substring(1)) >= 2; - isNewerThan1_19_R3 = Integer.parseInt(split[2].substring(1)) >= 3; - } - else isNewerThan1_19_R2 = false; + @Override + public boolean isVersionNewerThan1_19() { + return isNewerThan1_19; } - private void disableUseLessInfo() { - MinecraftVersion.disableBStats(); - MinecraftVersion.disableUpdateCheck(); - VersionChecker.hideOk = true; - try { - Field field = MinecraftVersion.class.getDeclaredField("version"); - field.setAccessible(true); - MinecraftVersion minecraftVersion; - try { - minecraftVersion = MinecraftVersion.valueOf(server_version.replace("v", "MC")); - } catch (IllegalArgumentException ex) { - minecraftVersion = MinecraftVersion.UNKNOWN; - } - field.set(MinecraftVersion.class, minecraftVersion); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - boolean hasGsonSupport; - try { - Class.forName("com.google.gson.Gson"); - hasGsonSupport = true; - } catch (Exception ex) { - hasGsonSupport = false; - } - try { - Field field= MinecraftVersion.class.getDeclaredField("hasGsonSupport"); - field.setAccessible(true); - field.set(Boolean.class, hasGsonSupport); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } + @Override + public boolean isVersionNewerThan1_19_R2() { + return isNewerThan1_19_R2; } - public void checkUpdate() { + @Override + public boolean isVersionNewerThan1_20() { + return isNewerThan1_20; + } + + @Override + public boolean isMojmap() { + return isMojmap; + } + + @Override + public CompletableFuture checkUpdate() { + CompletableFuture updateFuture = new CompletableFuture<>(); plugin.getScheduler().runTaskAsync(() -> { try { URL url = new URL("https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=2625&key=version"); @@ -113,28 +139,19 @@ public class VersionHelper { conn.setReadTimeout(60000); InputStream inputStream = conn.getInputStream(); String newest = new BufferedReader(new InputStreamReader(inputStream)).readLine(); - String current = plugin.getDescription().getVersion(); + String current = plugin.getVersionManager().getPluginVersion(); inputStream.close(); - if (!compareVer(newest, current)) { - AdventureUtils.consoleMessage(ConfigManager.lang.equalsIgnoreCase("chinese") ? "[CustomCrops] 当前已是最新版本" : "[CustomCrops] You are using the latest version."); + updateFuture.complete(false); return; } - - if (ConfigManager.lang.equalsIgnoreCase("chinese")) { - AdventureUtils.consoleMessage("[CustomCrops] 当前版本: " + current); - AdventureUtils.consoleMessage("[CustomCrops] 最新版本: " + newest); - AdventureUtils.consoleMessage("[CustomCrops] 请到 售后群https://polymart.org/resource/customcrops.2625 获取最新版本."); - } - else { - AdventureUtils.consoleMessage("[CustomCrops] Current version: " + current); - AdventureUtils.consoleMessage("[CustomCrops] Latest version: " + newest); - AdventureUtils.consoleMessage("[CustomCrops] Update is available: https://polymart.org/resource/customcrops.2625"); - } + updateFuture.complete(true); } catch (Exception exception) { - Log.warn("Error occurred when checking update"); + LogUtils.warn("Error occurred when checking update.", exception); + updateFuture.complete(false); } }); + return updateFuture; } private boolean compareVer(String newV, String currentV) { @@ -157,9 +174,7 @@ public class VersionHelper { } else if (newPart.length > 1 && currentPart.length > 1) { String[] newHotfix = newPart[1].split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)"); String[] currentHotfix = currentPart[1].split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)"); - // hotfix2 & hotfix if (newHotfix.length == 2 && currentHotfix.length == 1) return true; - // hotfix3 & hotfix2 else if (newHotfix.length > 1 && currentHotfix.length > 1) { int newHotfixNum = Integer.parseInt(newHotfix[1]); int currentHotfixNum = Integer.parseInt(currentHotfix[1]); @@ -181,15 +196,6 @@ public class VersionHelper { return false; } } - // if common parts are the same, the longer is newer return newVS.length > currentVS.length; } - - public boolean isSpigot() { - return isSpigot; - } - - public String getPluginVersion() { - return plugin.getDescription().getVersion(); - } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java new file mode 100644 index 0000000..3112e47 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/ActionManagerImpl.java @@ -0,0 +1,1130 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.action; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.event.CropBreakEvent; +import net.momirealms.customcrops.api.event.CropPlantEvent; +import net.momirealms.customcrops.api.event.PotBreakEvent; +import net.momirealms.customcrops.api.event.SprinklerBreakEvent; +import net.momirealms.customcrops.api.manager.*; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionExpansion; +import net.momirealms.customcrops.api.mechanic.action.ActionFactory; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.*; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.QualityCrop; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.Variation; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.YieldIncrease; +import net.momirealms.customcrops.api.mechanic.misc.Value; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; +import net.momirealms.customcrops.api.scheduler.CancellableTask; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.compatibility.VaultHook; +import net.momirealms.customcrops.manager.AdventureManagerImpl; +import net.momirealms.customcrops.manager.HologramManager; +import net.momirealms.customcrops.mechanic.item.impl.VariationCrop; +import net.momirealms.customcrops.mechanic.misc.TempFakeItem; +import net.momirealms.customcrops.mechanic.world.block.MemoryCrop; +import net.momirealms.customcrops.utils.ClassUtils; +import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.utils.EventUtils; +import net.momirealms.customcrops.utils.ItemUtils; +import org.bukkit.*; +import org.bukkit.block.BlockFace; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.ExperienceOrb; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +public class ActionManagerImpl implements ActionManager { + + private final CustomCropsPlugin plugin; + private final HashMap actionBuilderMap; + private final String EXPANSION_FOLDER = "expansions/action"; + + public ActionManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.actionBuilderMap = new HashMap<>(); + this.registerInbuiltActions(); + } + + private void registerInbuiltActions() { + this.registerMessageAction(); + this.registerCommandAction(); + this.registerMendingAction(); + this.registerExpAction(); + this.registerChainAction(); + this.registerPotionAction(); + this.registerSoundAction(); + this.registerPluginExpAction(); + this.registerTitleAction(); + this.registerActionBarAction(); + this.registerCloseInvAction(); + this.registerDelayedAction(); + this.registerConditionalAction(); + this.registerPriorityAction(); + this.registerLevelAction(); + this.registerFakeItemAction(); + this.registerFoodAction(); + this.registerItemAmountAction(); + this.registerItemDurabilityAction(); + this.registerGiveItemAction(); + this.registerMoneyAction(); + this.registerTimerAction(); + this.registerParticleAction(); + this.registerSwingHandAction(); + this.registerDropItemsAction(); + this.registerBreakAction(); + this.registerPlantAction(); + this.registerQualityCropsAction(); + this.registerVariationAction(); + this.registerForceTickAction(); + this.registerHologramAction(); + } + + @Override + public void load() { + loadExpansions(); + } + + @Override + public void unload() { + + } + + @Override + public void disable() { + actionBuilderMap.clear(); + } + + @Override + public boolean registerAction(String type, ActionFactory actionFactory) { + if (this.actionBuilderMap.containsKey(type)) return false; + this.actionBuilderMap.put(type, actionFactory); + return true; + } + + @Override + public boolean unregisterAction(String type) { + return this.actionBuilderMap.remove(type) != null; + } + + @Override + public Action getAction(ConfigurationSection section) { + ActionFactory factory = getActionFactory(section.getString("type")); + if (factory == null) { + LogUtils.warn("Action type: " + section.getString("type") + " doesn't exist."); + // to prevent NPE + return EmptyAction.instance; + } + return factory.build( + section.get("value"), + section.getDouble("chance", 1d) + ); + } + + @Override + @NotNull + public HashMap getActionMap(ConfigurationSection section) { + HashMap actionMap = new HashMap<>(); + if (section == null) return actionMap; + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + try { + actionMap.put( + ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)), + getActions(innerSection) + ); + } catch (IllegalArgumentException e) { + LogUtils.warn("Event: " + entry.getKey() + " doesn't exist!"); + } + } + } + return actionMap; + } + + @NotNull + @Override + public Action[] getActions(ConfigurationSection section) { + ArrayList actionList = new ArrayList<>(); + if (section == null) return actionList.toArray(new Action[0]); + + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + Action action = getAction(innerSection); + if (action != null) + actionList.add(action); + } + } + return actionList.toArray(new Action[0]); + } + + @Nullable + @Override + public ActionFactory getActionFactory(String type) { + return actionBuilderMap.get(type); + } + + private void registerHologramAction() { + registerAction("hologram", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + String text = section.getString("text", ""); + int duration = section.getInt("duration", 20); + double x = section.getDouble("x"); + double y = section.getDouble("y"); + double z = section.getDouble("z"); + boolean applyCorrection = section.getBoolean("apply-correction", false); + boolean onlyShowToOne = !section.getBoolean("visible-to-all", false); + return condition -> { + if (Math.random() > chance) return; + Location location = condition.getLocation().clone().add(x,y,z); + SimpleLocation simpleLocation = SimpleLocation.of(location); + if (applyCorrection) { + SimpleLocation cropLocation = simpleLocation.copy().add(0,1,0); + Optional crop = plugin.getWorldManager().getCropAt(cropLocation); + if (crop.isPresent()) { + WorldCrop worldCrop = crop.get(); + Crop config = worldCrop.getConfig(); + Crop.Stage stage = config.getStageByItemID(config.getStageItemByPoint(worldCrop.getPoint())); + if (stage != null) { + location.add(0, stage.getHologramOffset(), 0); + } + } + } + + ArrayList viewers = new ArrayList<>(); + if (onlyShowToOne) { + if (condition.getPlayer() == null) return; + viewers.add(condition.getPlayer()); + } else { + for (Player player : Bukkit.getOnlinePlayers()) { + if (simpleLocation.isNear(SimpleLocation.of(player.getLocation()), 48)) { + viewers.add(player); + } + } + } + Component component = AdventureManager.getInstance().getComponentFromMiniMessage(PlaceholderManager.getInstance().parse(condition.getPlayer(), text, condition.getArgs())); + for (Player viewer : viewers) { + HologramManager.getInstance().showHologram(viewer, location, component, duration * 50); + } + }; + } else { + LogUtils.warn("Illegal value format found at action: hologram"); + return EmptyAction.instance; + } + }); + } + + private void registerFakeItemAction() { + registerAction("fake-item", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + String item = section.getString("item", ""); + int duration = section.getInt("duration", 20); + double x = section.getDouble("x"); + double y = section.getDouble("y"); + double z = section.getDouble("z"); + boolean onlyShowToOne = !section.getBoolean("visible-to-all", true); + return condition -> { + if (Math.random() > chance) return; + if (item.equals("")) return; + Location location = condition.getLocation().clone().add(x,y,z); + new TempFakeItem(location, item, duration, onlyShowToOne ? condition.getPlayer() : null).start(); + }; + } else { + LogUtils.warn("Illegal value format found at action: fake-item"); + return EmptyAction.instance; + } + }); + } + + private void registerMessageAction() { + registerAction("message", (args, chance) -> { + ArrayList msg = ConfigUtils.stringListArgs(args); + return state -> { + if (Math.random() > chance) return; + List replaced = PlaceholderManager.getInstance().parse( + state.getPlayer(), + msg, + state.getArgs() + ); + for (String text : replaced) { + AdventureManagerImpl.getInstance().sendPlayerMessage(state.getPlayer(), text); + } + }; + }); + registerAction("broadcast", (args, chance) -> { + ArrayList msg = ConfigUtils.stringListArgs(args); + return state -> { + if (Math.random() > chance) return; + List replaced = PlaceholderManager.getInstance().parse( + state.getPlayer(), + msg, + state.getArgs() + ); + for (Player player : Bukkit.getOnlinePlayers()) { + for (String text : replaced) { + AdventureManager.getInstance().sendPlayerMessage(player, text); + } + } + }; + }); + } + + private void registerCommandAction() { + registerAction("command", (args, chance) -> { + ArrayList cmd = ConfigUtils.stringListArgs(args); + return state -> { + if (Math.random() > chance) return; + List replaced = PlaceholderManager.getInstance().parse( + state.getPlayer(), + cmd, + state.getArgs() + ); + plugin.getScheduler().runTaskSync(() -> { + for (String text : replaced) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), text); + } + }, state.getLocation()); + }; + }); + registerAction("random-command", (args, chance) -> { + ArrayList cmd = ConfigUtils.stringListArgs(args); + return state -> { + if (Math.random() > chance) return; + String random = cmd.get(ThreadLocalRandom.current().nextInt(cmd.size())); + random = PlaceholderManager.getInstance().parse(state.getPlayer(), random, state.getArgs()); + String finalRandom = random; + plugin.getScheduler().runTaskSync(() -> { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), finalRandom); + }, state.getLocation()); + }; + }); + } + + private void registerCloseInvAction() { + registerAction("close-inv", (args, chance) -> state -> { + if (Math.random() > chance) return; + state.getPlayer().closeInventory(); + }); + } + + private void registerActionBarAction() { + registerAction("actionbar", (args, chance) -> { + String text = (String) args; + return state -> { + if (Math.random() > chance) return; + String parsed = PlaceholderManager.getInstance().parse(state.getPlayer(), text, state.getArgs()); + AdventureManagerImpl.getInstance().sendActionbar(state.getPlayer(), parsed); + }; + }); + registerAction("random-actionbar", (args, chance) -> { + ArrayList texts = ConfigUtils.stringListArgs(args); + return state -> { + if (Math.random() > chance) return; + String random = texts.get(ThreadLocalRandom.current().nextInt(texts.size())); + random = PlaceholderManager.getInstance().parse(state.getPlayer(), random, state.getArgs()); + AdventureManagerImpl.getInstance().sendActionbar(state.getPlayer(), random); + }; + }); + } + + private void registerMendingAction() { + registerAction("mending", (args, chance) -> { + var value = ConfigUtils.getValue(args); + return state -> { + if (Math.random() > chance) return; + if (CustomCropsPlugin.get().getVersionManager().isSpigot()) { + state.getPlayer().getLocation().getWorld().spawn(state.getPlayer().getLocation(), ExperienceOrb.class, e -> e.setExperience((int) value.get(state.getPlayer()))); + } else { + state.getPlayer().giveExp((int) value.get(state.getPlayer()), true); + AdventureManagerImpl.getInstance().sendSound(state.getPlayer(), Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1f, 1f); + } + }; + }); + } + + private void registerFoodAction() { + registerAction("food", (args, chance) -> { + var value = ConfigUtils.getValue(args); + return state -> { + if (Math.random() > chance) return; + Player player = state.getPlayer(); + player.setFoodLevel((int) (player.getFoodLevel() + value.get(player))); + }; + }); + registerAction("saturation", (args, chance) -> { + var value = ConfigUtils.getValue(args); + return state -> { + if (Math.random() > chance) return; + Player player = state.getPlayer(); + player.setSaturation((float) (player.getSaturation() + value.get(player))); + }; + }); + } + + private void registerExpAction() { + registerAction("exp", (args, chance) -> { + var value = ConfigUtils.getValue(args); + return state -> { + if (Math.random() > chance) return; + state.getPlayer().giveExp((int) value.get(state.getPlayer())); + AdventureManagerImpl.getInstance().sendSound(state.getPlayer(), Sound.Source.PLAYER, Key.key("minecraft:entity.experience_orb.pickup"), 1, 1); + }; + }); + } + + private void registerSwingHandAction() { + registerAction("swing-hand", (args, chance) -> { + boolean arg = (boolean) args; + return state -> { + if (Math.random() > chance) return; + state.getPlayer().swingHand(arg ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND); + }; + }); + } + + private void registerForceTickAction() { + registerAction("force-tick", (args, chance) -> state -> { + if (Math.random() > chance) return; + Location location = state.getLocation(); + plugin.getWorldManager().getCustomCropsWorld(location.getWorld()) + .flatMap(world -> world.getChunkAt(ChunkCoordinate.getByBukkitChunk(location.getChunk()))) + .flatMap(chunk -> chunk.getBlockAt(SimpleLocation.of(location))) + .ifPresent(block -> { + block.tick(1); + if (block instanceof WorldSprinkler sprinkler) { + Sprinkler config = sprinkler.getConfig(); + state.setArg("{current}", String.valueOf(sprinkler.getWater())); + state.setArg("{water_bar}", config.getWaterBar() == null ? "" : config.getWaterBar().getWaterBar(sprinkler.getWater(), config.getStorage())); + } else if (block instanceof WorldPot pot) { + Pot config = pot.getConfig(); + state.setArg("{current}", String.valueOf(pot.getWater())); + state.setArg("{water_bar}", config.getWaterBar() == null ? "" : config.getWaterBar().getWaterBar(pot.getWater(), config.getStorage())); + state.setArg("{left_times}", String.valueOf(pot.getFertilizerTimes())); + state.setArg("{max_times}", String.valueOf(Optional.ofNullable(pot.getFertilizer()).map(Fertilizer::getTimes).orElse(0))); + state.setArg("{icon}", Optional.ofNullable(pot.getFertilizer()).map(Fertilizer::getIcon).orElse("")); + } + }); + }); + } + + private void registerVariationAction() { + registerAction("variation", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + ArrayList variationCrops = new ArrayList<>(); + for (String inner_key : section.getKeys(false)) { + VariationCrop variationCrop = new VariationCrop( + section.getString(inner_key + ".item"), + ItemCarrier.valueOf(section.getString(inner_key + ".type", "TripWire").toUpperCase(Locale.ENGLISH)), + section.getDouble(inner_key + ".chance") + ); + variationCrops.add(variationCrop); + } + VariationCrop[] variations = variationCrops.toArray(new VariationCrop[0]); + return state -> { + if (Math.random() > chance) return; + double bonus = 0; + Optional pot = plugin.getWorldManager().getPotAt(SimpleLocation.of(state.getLocation().clone().subtract(0,1,0))); + if (pot.isPresent()) { + Fertilizer fertilizer = pot.get().getFertilizer(); + if (fertilizer instanceof Variation variation) { + bonus += variation.getChanceBonus(); + } + } + for (VariationCrop variationCrop : variations) { + if (Math.random() < variationCrop.getChance() + bonus) { + plugin.getItemManager().removeAnythingAt(state.getLocation()); + plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(state.getLocation())); + plugin.getItemManager().placeItem(state.getLocation(), variationCrop.getItemCarrier(), variationCrop.getItemID()); + break; + } + } + }; + } else { + LogUtils.warn("Illegal value format found at action: variation"); + return EmptyAction.instance; + } + }); + } + + private void registerQualityCropsAction() { + registerAction("quality-crops", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + Value min = ConfigUtils.getValue(section.get("min")); + Value max = ConfigUtils.getValue(section.get("max")); + boolean toInv = section.getBoolean("to-inventory", false); + String[] qualityLoots = new String[ConfigManager.defaultQualityRatio().length]; + for (int i = 1; i <= ConfigManager.defaultQualityRatio().length; i++) { + qualityLoots[i-1] = section.getString("items." + i); + if (qualityLoots[i-1] == null) { + LogUtils.warn("items." + i + " should not be null"); + qualityLoots[i-1] = ""; + } + } + return state -> { + if (Math.random() > chance) return; + double[] ratio = ConfigManager.defaultQualityRatio(); + int random = (int) ThreadLocalRandom.current().nextDouble(min.get(state.getPlayer()), max.get(state.getPlayer()) + 1); + Optional pot = plugin.getWorldManager().getPotAt(SimpleLocation.of(state.getLocation().clone().subtract(0,1,0))); + if (pot.isPresent()) { + Fertilizer fertilizer = pot.get().getFertilizer(); + if (fertilizer instanceof YieldIncrease yieldIncrease) { + random += yieldIncrease.getAmountBonus(); + } else if (fertilizer instanceof QualityCrop qualityCrop && Math.random() < qualityCrop.getChance()) { + ratio = qualityCrop.getRatio(); + } + } + for (int i = 0; i < random; i++) { + double r1 = Math.random(); + for (int j = 0; j < ratio.length; j++) { + if (r1 < ratio[j]) { + ItemStack drop = plugin.getItemManager().getItemStack(state.getPlayer(), qualityLoots[j]); + if (drop == null || drop.getType() == Material.AIR) return; + if (toInv) { + ItemUtils.giveItem(state.getPlayer(), drop, 1); + } else { + state.getLocation().getWorld().dropItemNaturally(state.getLocation(), drop); + } + break; + } + } + } + }; + } else { + LogUtils.warn("Illegal value format found at action: quality-crops"); + return EmptyAction.instance; + } + }); + } + + private void registerDropItemsAction() { + registerAction("drop-items", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + boolean ignoreFertilizer = section.getBoolean("ignore-fertilizer", true); + String item = section.getString("item"); + Value min = ConfigUtils.getValue(section.get("min")); + Value max = ConfigUtils.getValue(section.get("max")); + boolean toInv = section.getBoolean("to-inventory", false); + return state -> { + if (Math.random() > chance) return; + ItemStack itemStack = plugin.getItemManager().getItemStack(state.getPlayer(), item); + if (itemStack != null) { + int random = (int) ThreadLocalRandom.current().nextDouble(min.get(state.getPlayer()), (max.get(state.getPlayer()) + 1)); + if (!ignoreFertilizer) { + Optional pot = plugin.getWorldManager().getPotAt(SimpleLocation.of(state.getLocation().clone().subtract(0,1,0))); + if (pot.isPresent()) { + Fertilizer fertilizer = pot.get().getFertilizer(); + if (fertilizer instanceof YieldIncrease yieldIncrease) { + random += yieldIncrease.getAmountBonus(); + } + } + } + itemStack.setAmount(random); + if (toInv) { + ItemUtils.giveItem(state.getPlayer(), itemStack, random); + } else { + state.getLocation().getWorld().dropItemNaturally(state.getLocation(), itemStack); + } + } else { + LogUtils.warn("Item: " + item + " doesn't exist"); + } + }; + } else { + LogUtils.warn("Illegal value format found at action: drop-items"); + return EmptyAction.instance; + } + }); + } + + private void registerPlantAction() { + registerAction("plant", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + int point = section.getInt("point", 0); + String key = section.getString("crop"); + return state -> { + if (Math.random() > chance) return; + if (key == null) return; + Crop crop = plugin.getItemManager().getCropByID(key); + if (crop == null) { + LogUtils.warn("Crop: " + key + " doesn't exist."); + return; + } + Location location = state.getLocation(); + Pot pot = plugin.getItemManager().getPotByBlock(location.getBlock().getRelative(BlockFace.DOWN)); + if (pot == null) { + plugin.debug("Crop should be planted on a pot at " + location); + return; + } + // check whitelist + if (!crop.getPotWhitelist().contains(pot.getKey())) { + crop.trigger(ActionTrigger.WRONG_POT, state); + return; + } + // check plant requirements + if (!RequirementManager.isRequirementMet(state, crop.getPlantRequirements())) { + return; + } + // check limitation + if (plugin.getWorldManager().isReachLimit(SimpleLocation.of(location), ItemType.CROP)) { + crop.trigger(ActionTrigger.REACH_LIMIT, state); + return; + } + // fire event + CropPlantEvent plantEvent = new CropPlantEvent(state.getPlayer(), state.getItemInHand(), location, crop, 0); + if (EventUtils.fireAndCheckCancel(plantEvent)) { + return; + } + // place the crop + switch (crop.getItemCarrier()) { + case ITEM_FRAME, ITEM_DISPLAY, TRIPWIRE -> plugin.getItemManager().placeItem(location, crop.getItemCarrier(), crop.getStageItemByPoint(point)); + default -> { + LogUtils.warn("Unsupported type for crop: " + crop.getItemCarrier().name()); + return; + } + } + plugin.getWorldManager().addCropAt(new MemoryCrop(SimpleLocation.of(location), crop.getKey(), point), SimpleLocation.of(location)); + }; + } else { + LogUtils.warn("Illegal value format found at action: plant"); + return EmptyAction.instance; + } + }); + } + + private void registerBreakAction() { + registerAction("break", (args, chance) -> { + boolean arg = (boolean) args; + return state -> { + if (Math.random() > chance) return; + Optional removed = plugin.getWorldManager().getBlockAt(SimpleLocation.of(state.getLocation())); + if (removed.isPresent()) { + switch (removed.get().getType()) { + case SPRINKLER -> { + WorldSprinkler sprinkler = (WorldSprinkler) removed.get(); + SprinklerBreakEvent event = new SprinklerBreakEvent(state.getPlayer(), state.getLocation(), sprinkler); + if (EventUtils.fireAndCheckCancel(event)) + return; + if (arg) sprinkler.getConfig().trigger(ActionTrigger.BREAK, state); + plugin.getItemManager().removeAnythingAt(state.getLocation()); + plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(state.getLocation())); + } + case CROP -> { + WorldCrop crop = (WorldCrop) removed.get(); + CropBreakEvent event = new CropBreakEvent(state.getPlayer(), state.getLocation(), crop); + if (EventUtils.fireAndCheckCancel(event)) + return; + Crop cropConfig = crop.getConfig(); + if (arg) { + cropConfig.trigger(ActionTrigger.BREAK, state); + cropConfig.getStageByItemID(cropConfig.getStageItemByPoint(crop.getPoint())).trigger(ActionTrigger.BREAK, state); + } + plugin.getItemManager().removeAnythingAt(state.getLocation()); + plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(state.getLocation())); + } + case POT -> { + WorldPot pot = (WorldPot) removed.get(); + PotBreakEvent event = new PotBreakEvent(state.getPlayer(), state.getLocation(), pot); + if (EventUtils.fireAndCheckCancel(event)) + return; + if (arg) pot.getConfig().trigger(ActionTrigger.BREAK, state); + plugin.getItemManager().removeAnythingAt(state.getLocation()); + plugin.getWorldManager().removeAnythingAt(SimpleLocation.of(state.getLocation())); + } + } + } else { + plugin.getItemManager().removeAnythingAt(state.getLocation()); + } + }; + }); + } + + private void registerParticleAction() { + registerAction("particle", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + Particle particleType = Particle.valueOf(section.getString("particle", "ASH").toUpperCase(Locale.ENGLISH)); + double x = section.getDouble("x",0); + double y = section.getDouble("y",0); + double z = section.getDouble("z",0); + double offSetX = section.getDouble("offset-x",0); + double offSetY = section.getDouble("offset-y",0); + double offSetZ = section.getDouble("offset-z",0); + int count = section.getInt("count", 1); + double extra = section.getDouble("extra", 0); + float scale = (float) section.getDouble("scale", 1d); + + ItemStack itemStack; + if (section.contains("itemStack")) + itemStack = CustomCropsPlugin.get() + .getItemManager() + .getItemStack(null, section.getString("itemStack")); + else + itemStack = null; + + Color color; + if (section.contains("color")) { + String[] rgb = section.getString("color","255,255,255").split(","); + color = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); + } else { + color = null; + } + + Color toColor; + if (section.contains("color")) { + String[] rgb = section.getString("to-color","255,255,255").split(","); + toColor = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); + } else { + toColor = null; + } + + return state -> { + if (Math.random() > chance) return; + state.getLocation().getWorld().spawnParticle( + particleType, + state.getLocation().getX() + x, + state.getLocation().getY() + y, + state.getLocation().getZ() + z, + count, + offSetX, + offSetY, + offSetZ, + extra, + itemStack != null ? + itemStack : + (color != null && toColor != null ? + new Particle.DustTransition(color, toColor, scale) : + (color != null ? + new Particle.DustOptions(color, scale) : + null + ) + ) + ); + }; + } else { + LogUtils.warn("Illegal value format found at action: particle"); + return EmptyAction.instance; + } + }); + } + + private void registerItemAmountAction() { + registerAction("item-amount", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + boolean mainOrOff = section.getString("hand", "main").equalsIgnoreCase("main"); + int amount = section.getInt("amount", 1); + return state -> { + if (Math.random() > chance) return; + Player player = state.getPlayer(); + ItemStack itemStack = mainOrOff ? player.getInventory().getItemInMainHand() : player.getInventory().getItemInOffHand(); + itemStack.setAmount(Math.max(0, itemStack.getAmount() + amount)); + }; + } else { + LogUtils.warn("Illegal value format found at action: item-amount"); + return EmptyAction.instance; + } + }); + } + + private void registerItemDurabilityAction() { + registerAction("durability", (args, chance) -> { + int amount = (int) args; + return state -> { + if (Math.random() > chance) return; + ItemStack itemStack = state.getItemInHand(); + if (amount > 0) { + ItemUtils.increaseDurability(itemStack, amount); + } else { + if (state.getPlayer().getGameMode() == GameMode.CREATIVE) return; + ItemUtils.decreaseDurability(state.getPlayer(), itemStack, -amount); + } + }; + }); + } + + private void registerGiveItemAction() { + registerAction("give-item", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + String id = section.getString("item"); + int amount = section.getInt("amount", 1); + return state -> { + if (Math.random() > chance) return; + Player player = state.getPlayer(); + ItemUtils.giveItem(player, Objects.requireNonNull(CustomCropsPlugin.get().getItemManager().getItemStack(player, id)), amount); + }; + } else { + LogUtils.warn("Illegal value format found at action: give-item"); + return EmptyAction.instance; + } + }); + } + + private void registerChainAction() { + registerAction("chain", (args, chance) -> { + List actions = new ArrayList<>(); + if (args instanceof ConfigurationSection section) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + actions.add(getAction(innerSection)); + } + } + } + return state -> { + if (Math.random() > chance) return; + for (Action action : actions) { + action.trigger(state); + } + }; + }); + } + + private void registerMoneyAction() { + registerAction("give-money", (args, chance) -> { + var value = ConfigUtils.getValue(args); + return state -> { + if (Math.random() > chance) return; + VaultHook.getEconomy().depositPlayer(state.getPlayer(), value.get(state.getPlayer())); + }; + }); + registerAction("take-money", (args, chance) -> { + var value = ConfigUtils.getValue(args); + return state -> { + if (Math.random() > chance) return; + VaultHook.getEconomy().withdrawPlayer(state.getPlayer(), value.get(state.getPlayer())); + }; + }); + } + + private void registerDelayedAction() { + registerAction("delay", (args, chance) -> { + List actions = new ArrayList<>(); + int delay; + boolean async; + if (args instanceof ConfigurationSection section) { + delay = section.getInt("delay", 1); + async = section.getBoolean("async", false); + ConfigurationSection actionSection = section.getConfigurationSection("actions"); + if (actionSection != null) { + for (Map.Entry entry : actionSection.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + actions.add(getAction(innerSection)); + } + } + } + } else { + delay = 1; + async = false; + } + return state -> { + if (Math.random() > chance) return; + if (async) { + plugin.getScheduler().runTaskSyncLater(() -> { + for (Action action : actions) { + action.trigger(state); + } + }, state.getLocation(), delay * 50L, TimeUnit.MILLISECONDS); + } else { + plugin.getScheduler().runTaskSyncLater(() -> { + for (Action action : actions) { + action.trigger(state); + } + }, state.getLocation(), delay * 50L, TimeUnit.MILLISECONDS); + } + }; + }); + } + + private void registerTimerAction() { + registerAction("timer", (args, chance) -> { + List actions = new ArrayList<>(); + int delay; + int duration; + int period; + boolean async; + if (args instanceof ConfigurationSection section) { + delay = section.getInt("delay", 2); + duration = section.getInt("duration", 20); + period = section.getInt("period", 2); + async = section.getBoolean("async", false); + ConfigurationSection actionSection = section.getConfigurationSection("actions"); + if (actionSection != null) { + for (Map.Entry entry : actionSection.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + actions.add(getAction(innerSection)); + } + } + } + } else { + delay = 1; + async = false; + duration = 20; + period = 1; + } + return state -> { + if (Math.random() > chance) return; + CancellableTask cancellableTask; + if (async) { + cancellableTask = plugin.getScheduler().runTaskAsyncTimer(() -> { + for (Action action : actions) { + action.trigger(state); + } + }, delay * 50L, period * 50L, TimeUnit.MILLISECONDS); + } else { + cancellableTask = plugin.getScheduler().runTaskSyncTimer(() -> { + for (Action action : actions) { + action.trigger(state); + } + }, state.getLocation(), delay, period); + } + plugin.getScheduler().runTaskSyncLater(cancellableTask::cancel, state.getLocation(), duration); + }; + }); + } + + private void registerTitleAction() { + registerAction("title", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + String title = section.getString("title"); + String subtitle = section.getString("subtitle"); + int fadeIn = section.getInt("fade-in", 20); + int stay = section.getInt("stay", 30); + int fadeOut = section.getInt("fade-out", 10); + return state -> { + if (Math.random() > chance) return; + AdventureManagerImpl.getInstance().sendTitle( + state.getPlayer(), + PlaceholderManager.getInstance().parse(state.getPlayer(), title, state.getArgs()), + PlaceholderManager.getInstance().parse(state.getPlayer(), subtitle, state.getArgs()), + fadeIn, + stay, + fadeOut + ); + }; + } else { + LogUtils.warn("Illegal value format found at action: title"); + return EmptyAction.instance; + } + }); + registerAction("random-title", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + List titles = section.getStringList("titles"); + if (titles.size() == 0) titles.add(""); + List subtitles = section.getStringList("subtitles"); + if (subtitles.size() == 0) subtitles.add(""); + int fadeIn = section.getInt("fade-in", 20); + int stay = section.getInt("stay", 30); + int fadeOut = section.getInt("fade-out", 10); + return state -> { + if (Math.random() > chance) return; + AdventureManagerImpl.getInstance().sendTitle( + state.getPlayer(), + PlaceholderManager.getInstance().parse(state.getPlayer(), titles.get(ThreadLocalRandom.current().nextInt(titles.size())), state.getArgs()), + PlaceholderManager.getInstance().parse(state.getPlayer(), subtitles.get(ThreadLocalRandom.current().nextInt(subtitles.size())), state.getArgs()), + fadeIn, + stay, + fadeOut + ); + }; + } else { + LogUtils.warn("Illegal value format found at action: random-title"); + return EmptyAction.instance; + } + }); + } + + private void registerPotionAction() { + registerAction("potion-effect", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + PotionEffect potionEffect = new PotionEffect( + Objects.requireNonNull(PotionEffectType.getByName(section.getString("type", "BLINDNESS").toUpperCase(Locale.ENGLISH))), + section.getInt("duration", 20), + section.getInt("amplifier", 0) + ); + return state -> { + if (Math.random() > chance) return; + state.getPlayer().addPotionEffect(potionEffect); + }; + } else { + LogUtils.warn("Illegal value format found at action: potion-effect"); + return EmptyAction.instance; + } + }); + } + + private void registerLevelAction() { + registerAction("level", (args, chance) -> { + var value = ConfigUtils.getValue(args); + return state -> { + if (Math.random() > chance) return; + Player player = state.getPlayer(); + player.setLevel((int) Math.max(0, player.getLevel() + value.get(state.getPlayer()))); + }; + }); + } + + @SuppressWarnings("all") + private void registerSoundAction() { + registerAction("sound", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + Sound sound = Sound.sound( + Key.key(section.getString("key")), + Sound.Source.valueOf(section.getString("source", "PLAYER").toUpperCase(Locale.ENGLISH)), + (float) section.getDouble("volume", 1), + (float) section.getDouble("pitch", 1) + ); + return state -> { + if (Math.random() > chance) return; + AdventureManagerImpl.getInstance().sendSound(state.getPlayer(), sound); + }; + } else { + LogUtils.warn("Illegal value format found at action: sound"); + return EmptyAction.instance; + } + }); + } + + private void registerConditionalAction() { + registerAction("conditional", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + Action[] actions = getActions(section.getConfigurationSection("actions")); + Requirement[] requirements = plugin.getRequirementManager().getRequirements(section.getConfigurationSection("conditions"), true); + return state -> { + if (Math.random() > chance) return; + if (requirements != null) + for (Requirement requirement : requirements) { + if (!requirement.isStateMet(state)) { + return; + } + } + for (Action action : actions) { + action.trigger(state); + } + }; + } else { + LogUtils.warn("Illegal value format found at action: conditional"); + return EmptyAction.instance; + } + }); + } + + private void registerPriorityAction() { + registerAction("priority", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + List> stateActionPairList = new ArrayList<>(); + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection inner) { + Action[] actions = getActions(inner.getConfigurationSection("actions")); + Requirement[] requirements = plugin.getRequirementManager().getRequirements(inner.getConfigurationSection("states"), false); + stateActionPairList.add(Pair.of(requirements, actions)); + } + } + return state -> { + if (Math.random() > chance) return; + outer: + for (Pair pair : stateActionPairList) { + if (pair.left() != null) + for (Requirement requirement : pair.left()) { + if (!requirement.isStateMet(state)) { + continue outer; + } + } + if (pair.right() != null) + for (Action action : pair.right()) { + action.trigger(state); + } + return; + } + }; + } else { + LogUtils.warn("Illegal value format found at action: priority"); + return EmptyAction.instance; + } + }); + } + + private void registerPluginExpAction() { + registerAction("plugin-exp", (args, chance) -> { + if (args instanceof ConfigurationSection section) { + String pluginName = section.getString("plugin"); + var value = ConfigUtils.getValue(section.get("exp")); + String target = section.getString("target"); + return state -> { + if (Math.random() > chance) return; + Optional.ofNullable(plugin.getIntegrationManager().getLevelPlugin(pluginName)).ifPresentOrElse(it -> { + it.addXp(state.getPlayer(), target, value.get(state.getPlayer())); + }, () -> LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation.")); + }; + } else { + LogUtils.warn("Illegal value format found at action: plugin-exp"); + return EmptyAction.instance; + } + }); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private void loadExpansions() { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + + List> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class expansionClass = ClassUtils.findClass(expansionJar, ActionExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class expansionClass : classes) { + ActionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterAction(expansion.getActionType()); + registerAction(expansion.getActionType(), expansion.getActionFactory()); + LogUtils.info("Loaded action expansion: " + expansion.getActionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor() ); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + LogUtils.warn("Error occurred when creating expansion instance.", e); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/EmptyAction.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/EmptyAction.java new file mode 100644 index 0000000..811f8b0 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/action/EmptyAction.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.action; + +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.requirement.State; + +public class EmptyAction implements Action { + + public static EmptyAction instance = new EmptyAction(); + + @Override + public void trigger(State state) { + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java new file mode 100644 index 0000000..6d530b4 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/ConditionManagerImpl.java @@ -0,0 +1,669 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.condition; + +import net.momirealms.biomeapi.BiomeAPI; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.manager.ConditionManager; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.mechanic.condition.Condition; +import net.momirealms.customcrops.api.mechanic.condition.ConditionExpansion; +import net.momirealms.customcrops.api.mechanic.condition.ConditionFactory; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.compatibility.papi.ParseUtils; +import net.momirealms.customcrops.mechanic.misc.CrowAttackAnimation; +import net.momirealms.customcrops.utils.ClassUtils; +import net.momirealms.customcrops.utils.ConfigUtils; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +public class ConditionManagerImpl implements ConditionManager { + + private final String EXPANSION_FOLDER = "expansions/condition"; + private final HashMap conditionBuilderMap; + private final CustomCropsPlugin plugin; + + public ConditionManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.conditionBuilderMap = new HashMap<>(); + this.registerInbuiltConditions(); + } + + @Override + public void load() { + this.loadExpansions(); + } + + @Override + public void unload() { + + } + + @Override + public void disable() { + this.conditionBuilderMap.clear(); + } + + private void registerInbuiltConditions() { + this.registerSeasonCondition(); + this.registerWaterCondition(); + this.registerTemperatureCondition(); + this.registerAndCondition(); + this.registerOrCondition(); + this.registerRandomCondition(); + this.registerEqualsCondition(); + this.registerNumberEqualCondition(); + this.registerRegexCondition(); + this.registerGreaterThanCondition(); + this.registerLessThanCondition(); + this.registerContainCondition(); + this.registerStartWithCondition(); + this.registerEndWithCondition(); + this.registerInListCondition(); + this.registerBiomeRequirement(); + this.registerFertilizerCondition(); + this.registerCrowAttackCondition(); + this.registerPotCondition(); + } + + @Override + public boolean registerCondition(String type, ConditionFactory conditionFactory) { + if (this.conditionBuilderMap.containsKey(type)) return false; + this.conditionBuilderMap.put(type, conditionFactory); + return true; + } + + @Override + public boolean unregisterCondition(String type) { + return this.conditionBuilderMap.remove(type) != null; + } + + @Override + public boolean hasCondition(String type) { + return conditionBuilderMap.containsKey(type); + } + + @Override + public @NotNull Condition[] getConditions(ConfigurationSection section) { + ArrayList conditions = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + String key = entry.getKey(); + if (hasCondition(key)) { + conditions.add(getCondition(key, innerSection)); + } else { + conditions.add(getCondition(section.getConfigurationSection(key))); + } + } + } + } + return conditions.toArray(new Condition[0]); + } + + @Override + public Condition getCondition(ConfigurationSection section) { + if (section == null) { + LogUtils.warn("Condition section should not be null"); + return EmptyCondition.instance; + } + return getCondition(section.getString("type"), section.get("value")); + } + + @Override + public Condition getCondition(String key, Object args) { + if (key == null) { + LogUtils.warn("Condition type should not be null"); + return EmptyCondition.instance; + } + ConditionFactory factory = getConditionFactory(key); + if (factory == null) { + LogUtils.warn("Condition type: " + key + " doesn't exist."); + return EmptyCondition.instance; + } + return factory.build(args); + } + + @Nullable + @Override + public ConditionFactory getConditionFactory(String type) { + return conditionBuilderMap.get(type); + } + + private void registerCrowAttackCondition() { + registerCondition("crow_attack", (args -> { + if (args instanceof ConfigurationSection section) { + String flyModel = section.getString("fly-model"); + String standModel = section.getString("stand-model"); + double chance = section.getDouble("chance"); + return block -> { + if (Math.random() > chance) return false; + SimpleLocation location = block.getLocation(); + if (ConfigManager.enableScarecrow()) { + int range = ConfigManager.scarecrowRange(); + Optional world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName()); + if (world.isEmpty()) return false; + CustomCropsWorld customCropsWorld = world.get(); + for (int i = -range; i <= range; i++) { + for (int j = -range; j <= range; j++) { + for (int k : new int[]{0,-1,1}) { + if (customCropsWorld.getScarecrowAt(location.copy().add(i, k, j)).isPresent()) { + return false; + } + } + } + } + } + new CrowAttackAnimation(location, flyModel, standModel).start(); + return true; + }; + } else { + LogUtils.warn("Wrong value format found at crow-attack condition."); + return EmptyCondition.instance; + } + })); + } + + private void registerBiomeRequirement() { + registerCondition("biome", (args) -> { + HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); + return block -> { + String currentBiome = BiomeAPI.getBiomeAt(block.getLocation().getBukkitLocation()); + return biomes.contains(currentBiome); + }; + }); + registerCondition("!biome", (args) -> { + HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); + return block -> { + String currentBiome = BiomeAPI.getBiomeAt(block.getLocation().getBukkitLocation()); + return !biomes.contains(currentBiome); + }; + }); + } + + private void registerRandomCondition() { + registerCondition("random", (args -> { + double value = ConfigUtils.getDoubleValue(args); + return block -> Math.random() < value; + })); + } + + private void registerPotCondition() { + registerCondition("pot", (args -> { + HashSet pots = new HashSet<>(ConfigUtils.stringListArgs(args)); + return block -> { + Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); + return worldPot.filter(pot -> pots.contains(pot.getKey())).isPresent(); + }; + })); + registerCondition("!pot", (args -> { + HashSet pots = new HashSet<>(ConfigUtils.stringListArgs(args)); + return block -> { + Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); + return worldPot.filter(pot -> !pots.contains(pot.getKey())).isPresent(); + }; + })); + } + + private void registerFertilizerCondition() { + registerCondition("fertilizer", (args -> { + HashSet fertilizer = new HashSet<>(ConfigUtils.stringListArgs(args)); + return block -> { + Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); + return worldPot.filter(pot -> { + Fertilizer fertilizerInstance = pot.getFertilizer(); + if (fertilizerInstance == null) return false; + return fertilizer.contains(fertilizerInstance.getKey()); + }).isPresent(); + }; + })); + registerCondition("fertilizer_type", (args -> { + HashSet fertilizer = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(str -> str.toUpperCase(Locale.ENGLISH)).toList()); + return block -> { + Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); + return worldPot.filter(pot -> { + Fertilizer fertilizerInstance = pot.getFertilizer(); + if (fertilizerInstance == null) return false; + return fertilizer.contains(fertilizerInstance.getFertilizerType().name()); + }).isPresent(); + }; + })); + } + + private void registerAndCondition() { + registerCondition("&&", (args -> { + if (args instanceof ConfigurationSection section) { + Condition[] conditions = getConditions(section); + return block -> ConditionManager.isConditionMet(block, conditions); + } else { + LogUtils.warn("Wrong value format found at && condition."); + return EmptyCondition.instance; + } + })); + } + + private void registerOrCondition() { + registerCondition("||", (args -> { + if (args instanceof ConfigurationSection section) { + Condition[] conditions = getConditions(section); + return block -> { + for (Condition condition : conditions) { + if (condition.isConditionMet(block)) { + return true; + } + } + return false; + }; + } else { + LogUtils.warn("Wrong value format found at || condition."); + return EmptyCondition.instance; + } + })); + } + + private void registerTemperatureCondition() { + registerCondition("temperature", (args) -> { + List> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); + return block -> { + SimpleLocation location = block.getLocation(); + World world = location.getBukkitWorld(); + if (world == null) return false; + double temp = world.getTemperature(location.getX(), location.getY(), location.getZ()); + for (Pair pair : tempPairs) { + if (temp >= pair.left() && temp <= pair.right()) { + return true; + } + } + return false; + }; + }); + } + + @SuppressWarnings("DuplicatedCode") + private void registerGreaterThanCondition() { + registerCondition(">=", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return Double.parseDouble(p1) >= Double.parseDouble(p2); + }; + } else { + LogUtils.warn("Wrong value format found at >= requirement."); + return EmptyCondition.instance; + } + }); + registerCondition(">", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return Double.parseDouble(p1) > Double.parseDouble(p2); + }; + } else { + LogUtils.warn("Wrong value format found at > requirement."); + return EmptyCondition.instance; + } + }); + } + + private void registerRegexCondition() { + registerCondition("regex", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("papi", ""); + String v2 = section.getString("regex", ""); + return block -> ParseUtils.setPlaceholders(null, v1).matches(v2); + } else { + LogUtils.warn("Wrong value format found at regex requirement."); + return EmptyCondition.instance; + } + }); + } + + private void registerNumberEqualCondition() { + registerCondition("==", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return Double.parseDouble(p1) == Double.parseDouble(p2); + }; + } else { + LogUtils.warn("Wrong value format found at !startsWith requirement."); + return EmptyCondition.instance; + } + }); + registerCondition("!=", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return Double.parseDouble(p1) != Double.parseDouble(p2); + }; + } else { + LogUtils.warn("Wrong value format found at !startsWith requirement."); + return EmptyCondition.instance; + } + }); + } + + @SuppressWarnings("DuplicatedCode") + private void registerLessThanCondition() { + registerCondition("<", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return Double.parseDouble(p1) < Double.parseDouble(p2); + }; + } else { + LogUtils.warn("Wrong value format found at < requirement."); + return EmptyCondition.instance; + } + }); + registerCondition("<=", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return Double.parseDouble(p1) <= Double.parseDouble(p2); + }; + } else { + LogUtils.warn("Wrong value format found at <= requirement."); + return EmptyCondition.instance; + } + }); + } + + private void registerStartWithCondition() { + registerCondition("startsWith", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return p1.startsWith(p2); + }; + } else { + LogUtils.warn("Wrong value format found at startsWith requirement."); + return EmptyCondition.instance; + } + }); + registerCondition("!startsWith", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return !p1.startsWith(p2); + }; + } else { + LogUtils.warn("Wrong value format found at !startsWith requirement."); + return EmptyCondition.instance; + } + }); + } + + private void registerEndWithCondition() { + registerCondition("endsWith", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return p1.endsWith(p2); + }; + } else { + LogUtils.warn("Wrong value format found at endsWith requirement."); + return EmptyCondition.instance; + } + }); + registerCondition("!endsWith", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return !p1.endsWith(p2); + }; + } else { + LogUtils.warn("Wrong value format found at !endsWith requirement."); + return EmptyCondition.instance; + } + }); + } + + private void registerContainCondition() { + registerCondition("contains", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return p1.contains(p2); + }; + } else { + LogUtils.warn("Wrong value format found at contains requirement."); + return EmptyCondition.instance; + } + }); + registerCondition("!contains", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return !p1.contains(p2); + }; + } else { + LogUtils.warn("Wrong value format found at !contains requirement."); + return EmptyCondition.instance; + } + }); + } + + private void registerInListCondition() { + registerCondition("in-list", (args) -> { + if (args instanceof ConfigurationSection section) { + String papi = section.getString("papi", ""); + HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); + return block -> { + String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(null, papi) : papi; + return values.contains(p1); + }; + } else { + LogUtils.warn("Wrong value format found at in-list requirement."); + return EmptyCondition.instance; + } + }); + registerCondition("!in-list", (args) -> { + if (args instanceof ConfigurationSection section) { + String papi = section.getString("papi", ""); + HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); + return block -> { + String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(null, papi) : papi; + return !values.contains(p1); + }; + } else { + LogUtils.warn("Wrong value format found at in-list requirement."); + return EmptyCondition.instance; + } + }); + } + + private void registerEqualsCondition() { + registerCondition("equals", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return p1.equals(p2); + }; + } else { + LogUtils.warn("Wrong value format found at equals requirement."); + return EmptyCondition.instance; + } + }); + registerCondition("!equals", (args) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return block -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(null, v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(null, v2) : v2; + return !p1.equals(p2); + }; + } else { + LogUtils.warn("Wrong value format found at !equals requirement."); + return EmptyCondition.instance; + } + }); + } + + private void registerSeasonCondition() { + registerCondition("suitable_season", (args) -> { + HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); + return block -> { + Season season = plugin.getIntegrationManager().getSeason(block.getLocation().getBukkitWorld()); + if (season == null) { + return true; + } + if (seasons.contains(season.name())) { + return true; + } + if (ConfigManager.enableGreenhouse()) { + SimpleLocation location = block.getLocation(); + Optional world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName()); + if (world.isEmpty()) return false; + CustomCropsWorld customCropsWorld = world.get(); + for (int i = 1, range = ConfigManager.greenhouseRange(); i <= range; i++) { + if (customCropsWorld.getGlassAt(location.copy().add(0,i,0)).isPresent()) { + return true; + } + } + } + return false; + }; + }); + registerCondition("unsuitable_season", (args) -> { + HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(it -> it.toUpperCase(Locale.ENGLISH)).toList()); + return block -> { + Season season = plugin.getIntegrationManager().getSeason(block.getLocation().getBukkitWorld()); + if (season == null) { + return false; + } + if (seasons.contains(season.name())) { + if (ConfigManager.enableGreenhouse()) { + SimpleLocation location = block.getLocation(); + Optional world = plugin.getWorldManager().getCustomCropsWorld(location.getWorldName()); + if (world.isEmpty()) return false; + CustomCropsWorld customCropsWorld = world.get(); + for (int i = 1, range = ConfigManager.greenhouseRange(); i <= range; i++) { + if (customCropsWorld.getGlassAt(location.copy().add(0,i,0)).isPresent()) { + return false; + } + } + } + return true; + } + return false; + }; + }); + } + + private void registerWaterCondition() { + registerCondition("water_more_than", (args) -> { + int value = (int) args; + return block -> { + Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); + return worldPot.filter(pot -> pot.getWater() > value).isPresent(); + }; + }); + registerCondition("water_less_than", (args) -> { + int value = (int) args; + return block -> { + Optional worldPot = plugin.getWorldManager().getPotAt(block.getLocation().copy().add(0,-1,0)); + return worldPot.filter(pot -> pot.getWater() < value).isPresent(); + }; + }); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private void loadExpansions() { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + + List> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class expansionClass = ClassUtils.findClass(expansionJar, ConditionExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class expansionClass : classes) { + ConditionExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterCondition(expansion.getConditionType()); + registerCondition(expansion.getConditionType(), expansion.getConditionFactory()); + LogUtils.info("Loaded condition expansion: " + expansion.getConditionType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor()); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + LogUtils.warn("Error occurred when creating expansion instance.", e); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/ExpressionAnd.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java similarity index 65% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/ExpressionAnd.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java index 6381c00..77de905 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/ExpressionAnd.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/condition/EmptyCondition.java @@ -15,19 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.requirement.papi; +package net.momirealms.customcrops.mechanic.condition; -import org.bukkit.entity.Player; +import net.momirealms.customcrops.api.mechanic.condition.Condition; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; -import java.util.List; +public class EmptyCondition implements Condition { -public record ExpressionAnd(List requirements) implements PapiRequirement{ + public static EmptyCondition instance = new EmptyCondition(); @Override - public boolean isMet(Player player) { - for (PapiRequirement requirement : requirements) { - if (!requirement.isMet(player)) return false; - } + public boolean isConditionMet(CustomCropsBlock block) { return true; } -} \ No newline at end of file +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/AbstractEventItem.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/AbstractEventItem.java new file mode 100644 index 0000000..92685f1 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/AbstractEventItem.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item; + +import net.momirealms.customcrops.api.common.item.EventItem; +import net.momirealms.customcrops.api.manager.ActionManager; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.requirement.State; + +import java.util.HashMap; + +public abstract class AbstractEventItem implements EventItem { + + private final HashMap actionMap; + + public AbstractEventItem(HashMap actionMap) { + this.actionMap = actionMap; + } + + @Override + public void trigger(ActionTrigger actionTrigger, State state) { + Action[] actions = actionMap.get(actionTrigger); + if (actions != null) { + ActionManager.triggerActions(state, actions); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java new file mode 100644 index 0000000..362a662 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/CustomProvider.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item; + +import net.momirealms.customcrops.api.manager.VersionManager; +import net.momirealms.customcrops.utils.ConfigUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Collection; + +public interface CustomProvider { + + boolean removeBlock(Location location); + + void placeCustomBlock(Location location, String id); + + default void placeBlock(Location location, String id) { + if (ConfigUtils.isVanillaItem(id)) { + location.getBlock().setType(Material.valueOf(id)); + } else { + placeCustomBlock(location, id); + } + } + + void placeFurniture(Location location, String id); + + void removeFurniture(Entity entity); + + String getBlockID(Block block); + + String getItemID(ItemStack itemInHand); + + ItemStack getItemStack(String id); + + String getEntityID(Entity entity); + + boolean isFurniture(Entity entity); + + default boolean isAir(Location location) { + Block block = location.getBlock(); + if (block.getType() != Material.AIR) + return false; + Location center = location.toCenterLocation(); + Collection entities = center.getWorld().getNearbyEntities(center, 0.5,0.51,0.5); + entities.removeIf(entity -> entity instanceof Player); + return entities.size() == 0; + } + + default void removeAnythingAt(Location location) { + if (!removeBlock(location)) { + Collection entities = location.getWorld().getNearbyEntities(location.toCenterLocation(), 0.5,0.5,0.5); + entities.removeIf(entity -> { + EntityType type = entity.getType(); + return type != EntityType.ITEM_FRAME + && (!VersionManager.isHigherThan1_19_R3() || type != EntityType.ITEM_DISPLAY); + }); + for (Entity entity : entities) { + removeFurniture(entity); + } + } + } + + default String getSomethingAt(Location location) { + Block block = location.getBlock(); + if (block.getType() != Material.AIR) { + return getBlockID(block); + } else { + Collection entities = location.getWorld().getNearbyEntities(location.toCenterLocation(), 0.5,0.5,0.5); + for (Entity entity : entities) { + if (isFurniture(entity)) { + return getEntityID(entity); + } + } + } + return "AIR"; + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java new file mode 100644 index 0000000..f7ad948 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/ItemManagerImpl.java @@ -0,0 +1,2355 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item; + +import com.google.common.base.Preconditions; +import net.momirealms.antigrieflib.AntiGriefLib; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.event.*; +import net.momirealms.customcrops.api.integration.ItemLibrary; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.ItemManager; +import net.momirealms.customcrops.api.manager.RequirementManager; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.condition.Conditions; +import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; +import net.momirealms.customcrops.api.mechanic.item.*; +import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; +import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; +import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; +import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderListener; +import net.momirealms.customcrops.mechanic.item.custom.itemsadder.ItemsAdderProvider; +import net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenListener; +import net.momirealms.customcrops.mechanic.item.custom.oraxen.OraxenProvider; +import net.momirealms.customcrops.mechanic.item.function.CFunction; +import net.momirealms.customcrops.mechanic.item.function.FunctionResult; +import net.momirealms.customcrops.mechanic.item.function.FunctionTrigger; +import net.momirealms.customcrops.mechanic.item.function.wrapper.*; +import net.momirealms.customcrops.mechanic.item.impl.CropConfig; +import net.momirealms.customcrops.mechanic.item.impl.PotConfig; +import net.momirealms.customcrops.mechanic.item.impl.SprinklerConfig; +import net.momirealms.customcrops.mechanic.item.impl.WateringCanConfig; +import net.momirealms.customcrops.mechanic.item.impl.fertilizer.*; +import net.momirealms.customcrops.mechanic.world.block.*; +import net.momirealms.customcrops.utils.ConfigUtils; +import net.momirealms.customcrops.utils.EventUtils; +import net.momirealms.customcrops.utils.ItemUtils; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.Waterlogged; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.*; + +public class ItemManagerImpl implements ItemManager { + + private final CustomCropsPlugin plugin; + private AbstractCustomListener listener; + private CustomProvider customProvider; + private final HashMap itemLibraryMap; + private ItemLibrary[] itemDetectionArray; + private final HashMap>> itemID2FunctionMap; + private final HashMap id2WateringCanMap; + private final HashMap item2WateringCanMap; + private final HashMap id2SprinklerMap; + private final HashMap twoDItem2SprinklerMap; + private final HashMap threeDItem2SprinklerMap; + private final HashMap id2PotMap; + private final HashMap item2PotMap; + private final HashMap stage2CropMap; + private final HashMap seed2CropMap; + private final HashMap id2CropMap; + private final HashMap stage2CropStageMap; + private final HashMap id2FertilizerMap; + private final HashMap item2FertilizerMap; + private final AntiGriefLib antiGrief; + private final HashSet deadCrops; + + public ItemManagerImpl(CustomCropsPlugin plugin, AntiGriefLib antiGriefLib) { + this.plugin = plugin; + this.antiGrief = antiGriefLib; + this.itemLibraryMap = new HashMap<>(); + this.itemID2FunctionMap = new HashMap<>(); + this.id2WateringCanMap = new HashMap<>(); + this.item2WateringCanMap = new HashMap<>(); + this.id2SprinklerMap = new HashMap<>(); + this.twoDItem2SprinklerMap = new HashMap<>(); + this.threeDItem2SprinklerMap = new HashMap<>(); + this.id2PotMap = new HashMap<>(); + this.item2PotMap = new HashMap<>(); + this.stage2CropMap = new HashMap<>(); + this.seed2CropMap = new HashMap<>(); + this.id2CropMap = new HashMap<>(); + this.id2FertilizerMap = new HashMap<>(); + this.item2FertilizerMap = new HashMap<>(); + this.stage2CropStageMap = new HashMap<>(); + this.deadCrops = new HashSet<>(); + if (Bukkit.getPluginManager().isPluginEnabled("Oraxen")) { + listener = new OraxenListener(this); + customProvider = new OraxenProvider(); + } else if (Bukkit.getPluginManager().isPluginEnabled("ItemsAdder")) { + listener = new ItemsAdderListener(this); + customProvider = new ItemsAdderProvider(); + } else { + LogUtils.severe("======================================================"); + LogUtils.severe(" Please install ItemsAdder or Oraxen as dependency."); + LogUtils.severe(" ItemsAdder: https://www.spigotmc.org/resources/73355/"); + LogUtils.severe(" Oraxen: https://www.spigotmc.org/resources/72448/"); + LogUtils.severe("======================================================"); + Bukkit.getPluginManager().disablePlugin(plugin); + } + } + + @Override + public void load() { + this.loadItems(); + if (this.listener != null) { + Bukkit.getPluginManager().registerEvents(this.listener, this.plugin); + } + this.resetItemDetectionOrder(); + } + + @Override + public void unload() { + if (this.listener != null) { + HandlerList.unregisterAll(this.listener); + } + this.itemID2FunctionMap.clear(); + this.id2WateringCanMap.clear(); + this.item2WateringCanMap.clear(); + this.id2SprinklerMap.clear(); + this.twoDItem2SprinklerMap.clear(); + this.threeDItem2SprinklerMap.clear(); + this.id2PotMap.clear(); + this.item2PotMap.clear(); + this.stage2CropMap.clear(); + this.seed2CropMap.clear(); + this.id2CropMap.clear(); + this.stage2CropStageMap.clear(); + this.id2FertilizerMap.clear(); + this.item2FertilizerMap.clear(); + this.deadCrops.clear(); + CFunction.resetID(); + } + + @Override + public void disable() { + unload(); + this.itemLibraryMap.clear(); + } + + @Override + public boolean registerItemLibrary(@NotNull ItemLibrary itemLibrary) { + if (this.itemLibraryMap.containsKey(itemLibrary.identification())) + return false; + this.itemLibraryMap.put(itemLibrary.identification(), itemLibrary); + resetItemDetectionOrder(); + return true; + } + + @Override + public boolean unregisterItemLibrary(String identification) { + boolean success = this.itemLibraryMap.remove(identification) != null; + if (success) { + resetItemDetectionOrder(); + } + return success; + } + + private void resetItemDetectionOrder() { + ArrayList itemLibraries = new ArrayList<>(); + for (String identification : ConfigManager.itemDetectionOrder()) { + ItemLibrary library = itemLibraryMap.get(identification); + if (library != null) { + itemLibraries.add(library); + } + } + this.itemDetectionArray = itemLibraries.toArray(new ItemLibrary[0]); + } + + @Override + public String getItemID(ItemStack itemStack) { + if (itemStack == null) + return "AIR"; + String id; + id = customProvider.getItemID(itemStack); + if (id != null) return id; + else { + for (ItemLibrary library : itemDetectionArray) { + id = library.getItemID(itemStack); + if (id != null) + return id; + } + } + return itemStack.getType().name(); + } + + @Override + public ItemStack getItemStack(Player player, String id) { + ItemStack itemStack; + itemStack = customProvider.getItemStack(id); + if (itemStack != null) + return itemStack; + if (id.contains(":")) { + String[] split = id.split(":", 2); + return itemLibraryMap.get(split[0]).buildItem(player, split[1]); + } else { + try { + return new ItemStack(Material.valueOf(id.toUpperCase(Locale.ENGLISH))); + } catch (IllegalArgumentException e) { + return new ItemStack(Material.AIR); + } + } + } + + public boolean registerFertilizer(@NotNull Fertilizer fertilizer) { + if (this.id2FertilizerMap.containsKey(fertilizer.getKey())) { + return false; + } + this.id2FertilizerMap.put(fertilizer.getKey(), fertilizer); + if (this.item2FertilizerMap.put(fertilizer.getItemID(), fertilizer) != null) { + LogUtils.warn("Item " + fertilizer.getItemID() + " has more than one fertilizer config."); + return false; + } + return true; + } + + public boolean registerWateringCan(@NotNull WateringCan wateringCan) { + if (this.id2WateringCanMap.containsKey(wateringCan.getKey())) { + return false; + } + this.id2WateringCanMap.put(wateringCan.getKey(), wateringCan); + if (this.item2WateringCanMap.put(wateringCan.getItemID(), wateringCan) != null) { + LogUtils.warn("Item " + wateringCan.getItemID() + " has more than one watering-can config."); + return false; + } + return true; + } + + public boolean registerPot(@NotNull Pot pot) { + if (this.id2PotMap.containsKey(pot.getKey())) { + return false; + } + this.id2PotMap.put(pot.getKey(), pot); + for (String block : pot.getPotBlocks()) { + if (this.item2PotMap.put(block, pot) != null) { + LogUtils.warn("Block " + block + " has more than one pot config."); + return false; + } + } + return true; + } + + public boolean registerCrop(@NotNull Crop crop) { + if (this.id2CropMap.containsKey(crop.getKey())) { + return false; + } + this.id2CropMap.put(crop.getKey(), crop); + if (this.seed2CropMap.put(crop.getSeedItemID(), crop) != null) { + LogUtils.warn("Item " + crop.getSeedItemID() + " has more than one crop config."); + return false; + } + for (Crop.Stage stage : crop.getStages()) { + if (stage.getStageID() != null) { + if (this.stage2CropMap.put(stage.getStageID(), crop) != null) { + LogUtils.warn("Item " + stage.getStageID() + " has more than one crop config."); + return false; + } + if (this.stage2CropStageMap.put(stage.getStageID(), stage) != null) { + LogUtils.warn("Item " + stage.getStageID() + " has more than one crop config."); + return false; + } + } + } + return true; + } + + public boolean registerSprinkler(@NotNull Sprinkler sprinkler) { + if (this.id2SprinklerMap.containsKey(sprinkler.getKey())) { + return false; + } + this.id2SprinklerMap.put(sprinkler.getKey(), sprinkler); + if (sprinkler.get2DItemID() != null) { + if (this.twoDItem2SprinklerMap.put(sprinkler.get2DItemID(), sprinkler) != null) { + LogUtils.warn("Item " + sprinkler.get2DItemID() + " has more than one sprinkler config."); + return false; + } + } + if (this.threeDItem2SprinklerMap.put(sprinkler.get3DItemID(), sprinkler) != null) { + LogUtils.warn("Item " + sprinkler.get3DItemID() + " has more than one sprinkler config."); + return false; + } + return true; + } + + @Override + public void placeItem(Location location, ItemCarrier carrier, String id) { + switch (carrier) { + case ITEM_DISPLAY, ITEM_FRAME -> { + customProvider.placeFurniture(location, id); + } + case TRIPWIRE, NOTE_BLOCK, CHORUS, MUSHROOM -> { + customProvider.placeBlock(location, id); + } + } + } + + @Override + public void removeAnythingAt(Location location) { + customProvider.removeAnythingAt(location); + } + + @Override + public WateringCan getWateringCanByID(@NotNull String id) { + return id2WateringCanMap.get(id); + } + + @Override + public WateringCan getWateringCanByItemID(@NotNull String id) { + return item2WateringCanMap.get(id); + } + + @Override + public WateringCan getWateringCanByItemStack(@NotNull ItemStack itemStack) { + if (itemStack.getType() == Material.AIR) + return null; + return getWateringCanByItemID(getItemID(itemStack)); + } + + @Override + public Sprinkler getSprinklerByID(@NotNull String id) { + return id2SprinklerMap.get(id); + } + + @Override + public Sprinkler getSprinklerBy3DItemID(@NotNull String id) { + return threeDItem2SprinklerMap.get(id); + } + + @Override + public Sprinkler getSprinklerBy2DItemID(@NotNull String id) { + return twoDItem2SprinklerMap.get(id); + } + + @Override + public Sprinkler getSprinklerByEntity(@NotNull Entity entity) { + return Optional.ofNullable(customProvider.getEntityID(entity)).map(threeDItem2SprinklerMap::get).orElse(null); + } + + @Override + public Sprinkler getSprinklerBy2DItemStack(@NotNull ItemStack itemStack) { + return getSprinklerBy2DItemID(getItemID(itemStack)); + } + + @Override + public Sprinkler getSprinklerBy3DItemStack(@NotNull ItemStack itemStack) { + return getSprinklerBy3DItemID(getItemID(itemStack)); + } + + @Override + public Pot getPotByID(@NotNull String id) { + return id2PotMap.get(id); + } + + @Override + public Pot getPotByBlockID(@NotNull String id) { + return item2PotMap.get(id); + } + + @Override + public Pot getPotByBlock(@NotNull Block block) { + return getPotByBlockID(customProvider.getBlockID(block)); + } + + @Override + public Pot getPotByItemStack(@NotNull ItemStack itemStack) { + return getPotByBlockID(getItemID(itemStack)); + } + + @Override + public Sprinkler getSprinklerByItemStack(@NotNull ItemStack itemStack) { + if (itemStack.getType() == Material.AIR) + return null; + return getSprinklerBy2DItemID(getItemID(itemStack)); + } + + @Override + public Fertilizer getFertilizerByID(@NotNull String id) { + return id2FertilizerMap.get(id); + } + + @Override + public Fertilizer getFertilizerByItemID(@NotNull String id) { + return item2FertilizerMap.get(id); + } + + @Override + public Fertilizer getFertilizerByItemStack(@NotNull ItemStack itemStack) { + if (itemStack.getType() == Material.AIR) + return null; + return item2FertilizerMap.get(getItemID(itemStack)); + } + + @Override + public Crop getCropByID(String id) { + return id2CropMap.get(id); + } + + @Override + public Crop getCropBySeedID(String id) { + return seed2CropMap.get(id); + } + + @Override + public Crop getCropBySeedItemStack(ItemStack itemStack) { + if (itemStack.getType() == Material.AIR) + return null; + return getCropByID(getItemID(itemStack)); + } + + @Override + public Crop getCropByStageID(String id) { + return stage2CropMap.get(id); + } + + @Override + public Crop getCropByEntity(Entity entity) { + return Optional.ofNullable(customProvider.getEntityID(entity)).map(stage2CropMap::get).orElse(null); + } + + @Override + public Crop getCropByBlock(Block block) { + return stage2CropMap.get(customProvider.getBlockID(block)); + } + + @Override + public Crop.Stage getCropStageByStageID(String id) { + return stage2CropStageMap.get(id); + } + + @SuppressWarnings("DuplicatedCode") + private void loadItems() { + for (String item : List.of("watering-cans", "pots", "crops", "sprinklers", "fertilizers")) { + File folder = new File(plugin.getDataFolder(), "contents" + File.separator + item); + if (!folder.exists()) { + plugin.saveResource("contents" + File.separator + item + File.separator + "default.yml", true); + ConfigUtils.addDefaultNamespace(new File(plugin.getDataFolder(), "contents" + File.separator + item + File.separator + "default.yml")); + } + List files = ConfigUtils.getFilesRecursively(new File(plugin.getDataFolder(), "contents" + File.separator + item)); + for (File file : files) { + YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + for (Map.Entry entry : config.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection section) { + switch (item) { + case "watering-cans" -> loadWateringCan(entry.getKey(), section); + case "pots" -> loadPot(entry.getKey(), section); + case "crops" -> loadCrop(entry.getKey(), section); + case "sprinklers" -> loadSprinkler(entry.getKey(), section); + case "fertilizers" -> loadFertilizer(entry.getKey(), section); + } + } + } + } + } + this.loadDeadCrops(); + if (ConfigManager.enableGreenhouse()) + this.loadGreenhouse(); + if (ConfigManager.enableScarecrow()) + this.loadScarecrow(); + } + + private void loadDeadCrops() { + // register functions for dead crops + // or just keep it empty and let ItemsAdder/Oraxen handle the events + for (String id : deadCrops) { + this.registerItemFunction(id, FunctionTrigger.BE_INTERACTED, + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { + return FunctionResult.PASS; + } + Player player = furnitureWrapper.getPlayer(); + Location cropLocation = furnitureWrapper.getLocation(); + ItemStack itemInHand = furnitureWrapper.getItemInHand(); + String itemID = getItemID(itemInHand); + int itemAmount = itemInHand.getAmount(); + Location potLocation = cropLocation.clone().subtract(0,1,0); + Pot pot = getPotByBlock(potLocation.getBlock()); + State potState = new State(player, itemInHand, potLocation); + // check pot use requirements + if (pot != null && RequirementManager.isRequirementMet(potState, pot.getUseRequirements())) { + // get water in pot + int waterInPot = plugin.getWorldManager().getPotAt(SimpleLocation.of(potLocation)).map(WorldPot::getWater).orElse(0); + // water the pot + for (PassiveFillMethod method : pot.getPassiveFillMethods()) { + if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { + if (method.canFill(potState)) { + if (waterInPot < pot.getStorage()) { + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemAmount - method.getUsedAmount()); + if (method.getReturned() != null) { + ItemStack returned = getItemStack(player, method.getReturned()); + ItemUtils.giveItem(player, returned, method.getReturnedAmount()); + } + } + method.trigger(potState); + pot.trigger(ActionTrigger.ADD_WATER, potState); + plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(potLocation), method.getAmount()); + } else { + pot.trigger(ActionTrigger.FULL, potState); + } + } + return FunctionResult.RETURN; + } + } + } + return FunctionResult.PASS; + }, CFunction.FunctionPriority.NORMAL), + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { + return FunctionResult.PASS; + } + Player player = blockWrapper.getPlayer(); + Location cropLocation = blockWrapper.getLocation(); + ItemStack itemInHand = blockWrapper.getItemInHand(); + String itemID = getItemID(itemInHand); + int itemAmount = itemInHand.getAmount(); + Location potLocation = cropLocation.clone().subtract(0,1,0); + Pot pot = getPotByBlock(potLocation.getBlock()); + State potState = new State(player, itemInHand, potLocation); + // check pot use requirements + if (pot != null && RequirementManager.isRequirementMet(potState, pot.getUseRequirements())) { + // get water in pot + int waterInPot = plugin.getWorldManager().getPotAt(SimpleLocation.of(potLocation)).map(WorldPot::getWater).orElse(0); + // water the pot + for (PassiveFillMethod method : pot.getPassiveFillMethods()) { + if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { + if (method.canFill(potState)) { + if (waterInPot < pot.getStorage()) { + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemAmount - method.getUsedAmount()); + if (method.getReturned() != null) { + ItemStack returned = getItemStack(player, method.getReturned()); + ItemUtils.giveItem(player, returned, method.getReturnedAmount()); + } + } + method.trigger(potState); + pot.trigger(ActionTrigger.ADD_WATER, potState); + plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(potLocation), method.getAmount()); + } else { + pot.trigger(ActionTrigger.FULL, potState); + } + } + return FunctionResult.RETURN; + } + } + } + return FunctionResult.PASS; + }, CFunction.FunctionPriority.NORMAL) + ); + } + } + + private void loadGreenhouse() { + this.registerItemFunction(ConfigManager.greenhouseID(), FunctionTrigger.PLACE, + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof PlaceWrapper placeWrapper)) { + return FunctionResult.PASS; + } + // fire event + GreenhouseGlassPlaceEvent event = new GreenhouseGlassPlaceEvent(placeWrapper.getPlayer(), placeWrapper.getLocation()); + if (EventUtils.fireAndCheckCancel(event)) + return FunctionResult.CANCEL_EVENT_AND_RETURN; + + SimpleLocation simpleLocation = SimpleLocation.of(placeWrapper.getLocation()); + plugin.getWorldManager().addGlassAt(new MemoryGlass(simpleLocation), simpleLocation); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + this.registerItemFunction(ConfigManager.greenhouseID(), FunctionTrigger.BREAK, + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof BreakWrapper breakWrapper)) { + return FunctionResult.PASS; + } + // fire event + GreenhouseGlassBreakEvent event = new GreenhouseGlassBreakEvent(breakWrapper.getPlayer(), breakWrapper.getLocation()); + if (EventUtils.fireAndCheckCancel(event)) + return FunctionResult.CANCEL_EVENT_AND_RETURN; + + plugin.getWorldManager().removeGlassAt(SimpleLocation.of(breakWrapper.getLocation())); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + } + + private void loadScarecrow() { + this.registerItemFunction(ConfigManager.scarecrowID(), FunctionTrigger.PLACE, + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof PlaceWrapper placeWrapper)) { + return FunctionResult.PASS; + } + // fire event + ScarecrowPlaceEvent event = new ScarecrowPlaceEvent(placeWrapper.getPlayer(), placeWrapper.getLocation()); + if (EventUtils.fireAndCheckCancel(event)) + return FunctionResult.CANCEL_EVENT_AND_RETURN; + + SimpleLocation simpleLocation = SimpleLocation.of(placeWrapper.getLocation()); + plugin.getWorldManager().addScarecrowAt(new MemoryScarecrow(simpleLocation), simpleLocation); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + this.registerItemFunction(ConfigManager.scarecrowID(), FunctionTrigger.BREAK, + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof BreakWrapper breakWrapper)) { + return FunctionResult.PASS; + } + // fire event + ScarecrowBreakEvent event = new ScarecrowBreakEvent(breakWrapper.getPlayer(), breakWrapper.getLocation()); + if (EventUtils.fireAndCheckCancel(event)) + return FunctionResult.CANCEL_EVENT_AND_RETURN; + + plugin.getWorldManager().removeScarecrowAt(SimpleLocation.of(breakWrapper.getLocation())); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + } + + @SuppressWarnings("DuplicatedCode") + private void loadWateringCan(String key, ConfigurationSection section) { + String itemID = section.getString("item"); + int width = section.getInt("effective-range.width"); + int length = section.getInt("effective-range.length"); + HashSet potWhiteList = new HashSet<>(section.getStringList("pot-whitelist")); + HashSet sprinklerWhiteList = new HashSet<>(section.getStringList("sprinkler-whitelist")); + boolean hasDynamicLore = section.getBoolean("dynamic-lore.enable", false); + List lore = section.getStringList("dynamic-lore.lore"); + + WateringCanConfig wateringCan = new WateringCanConfig( + key, + itemID, section.getBoolean("infinite", false), width, + length, section.getInt("capacity", 3), section.getInt("water", 1), + hasDynamicLore, lore, + potWhiteList, sprinklerWhiteList, + ConfigUtils.getPositiveFillMethods(section.getConfigurationSection("fill-method")), + ConfigUtils.getInt2IntMap(section.getConfigurationSection("appearance")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), + ConfigUtils.getActionMap(section.getConfigurationSection("events")), + section.contains("water-bar") ? WaterBar.of( + section.getString("water-bar.left", ""), + section.getString("water-bar.empty", ""), + section.getString("water-bar.full", ""), + section.getString("water-bar.right", "") + ) : null + ); + + if (!this.registerWateringCan(wateringCan)) { + LogUtils.warn("Failed to register new watering can: " + key + " due to duplicated entries."); + return; + } + + this.registerItemFunction(itemID, FunctionTrigger.INTERACT_AT, + /* + * Handle clicking pot with a watering can + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { + return FunctionResult.PASS; + } + // click the upper face + if (blockWrapper.getClickedFace() != BlockFace.UP) { + return FunctionResult.PASS; + } + // is a pot + Pot pot = getPotByBlock(blockWrapper.getClickedBlock()); + if (pot == null) { + return FunctionResult.PASS; + } + final Player player = blockWrapper.getPlayer();; + final ItemStack itemStack = blockWrapper.getItemInHand(); + final Location clicked = blockWrapper.getClickedBlock().getLocation(); + State state = new State(player, itemStack, clicked); + // check watering-can requirements + if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { + return FunctionResult.RETURN; + } + // check whitelist + if (!wateringCan.getPotWhitelist().contains(pot.getKey())) { + wateringCan.trigger(ActionTrigger.WRONG_POT, state); + return FunctionResult.RETURN; + } + // check amount of water + int waterInCan = wateringCan.getCurrentWater(itemStack); + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + if (waterInCan > 0 || wateringCan.isInfinite()) { + state.setArg("{current}", String.valueOf(waterInCan - 1)); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); + wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs()); + wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); + Collection pots = getPotInRange(clicked, wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey()); + for (Location location : pots) { + plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater()); + pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); + } + } else { + state.setArg("{current}", "0"); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); + wateringCan.trigger(ActionTrigger.NO_WATER, state); + } + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.HIGH), + /* + * Handle clicking crop with a watering can + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { + return FunctionResult.PASS; + } + // is a crop + String id = customProvider.getBlockID(blockWrapper.getClickedBlock()); + Crop crop = getCropByStageID(id); + if (crop == null && !deadCrops.contains(id)) { + return FunctionResult.PASS; + } + // get pot block + Block potBlock = blockWrapper.getClickedBlock().getRelative(BlockFace.DOWN); + Pot pot = getPotByBlock(potBlock); + if (pot == null) { + LogUtils.warn("Unexpected issue: Detetced that crops are not planted on a pot: " + blockWrapper.getClickedBlock().getLocation()); + return FunctionResult.RETURN; + } + + final Player player = blockWrapper.getPlayer();; + final ItemStack itemStack = blockWrapper.getItemInHand(); + final Location clicked = blockWrapper.getClickedBlock().getLocation(); + State state = new State(player, itemStack, clicked); + // check watering-can use requirements + if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { + return FunctionResult.RETURN; + } + // check crop interact requirements + if (crop != null && !RequirementManager.isRequirementMet(state, crop.getInteractRequirements())) { + return FunctionResult.RETURN; + } + // check pot use requirements + if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { + return FunctionResult.RETURN; + } + // check watering-can whitelist + if (!wateringCan.getPotWhitelist().contains(pot.getKey())) { + wateringCan.trigger(ActionTrigger.WRONG_POT, state); + return FunctionResult.RETURN; + } + // check amount of water + int waterInCan = wateringCan.getCurrentWater(itemStack); + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + if (waterInCan > 0 || wateringCan.isInfinite()) { + state.setArg("{current}", String.valueOf(waterInCan - 1)); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); + wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs()); + wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); + Collection pots = getPotInRange(potBlock.getLocation(), wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey()); + for (Location location : pots) { + plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater()); + pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); + } + } else { + state.setArg("{current}", "0"); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); + wateringCan.trigger(ActionTrigger.NO_WATER, state); + } + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL), + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { + return FunctionResult.PASS; + } + // is a crop + String id = furnitureWrapper.getID(); + Crop crop = getCropByStageID(id); + if (crop == null && !deadCrops.contains(id)) { + return FunctionResult.PASS; + } + // get pot block + Block potBlock = furnitureWrapper.getLocation().getBlock().getRelative(BlockFace.DOWN); + Pot pot = getPotByBlock(potBlock); + if (pot == null) { + LogUtils.warn("Unexpected issue: Detetced that crops are not planted on a pot: " + furnitureWrapper.getLocation()); + return FunctionResult.RETURN; + } + + final Player player = furnitureWrapper.getPlayer(); + final ItemStack itemStack = furnitureWrapper.getItemInHand(); + final Location clicked = furnitureWrapper.getLocation(); + State state = new State(player, itemStack, clicked); + // check watering-can use requirements + if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { + return FunctionResult.RETURN; + } + // check crop interact requirements + if (crop != null && !RequirementManager.isRequirementMet(state, crop.getInteractRequirements())) { + return FunctionResult.RETURN; + } + // check pot use requirements + if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { + return FunctionResult.RETURN; + } + // check watering-can whitelist + if (!wateringCan.getPotWhitelist().contains(pot.getKey())) { + wateringCan.trigger(ActionTrigger.WRONG_POT, state); + return FunctionResult.RETURN; + } + // check amount of water + int waterInCan = wateringCan.getCurrentWater(itemStack); + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + if (waterInCan > 0 || wateringCan.isInfinite()) { + state.setArg("{current}", String.valueOf(waterInCan - 1)); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); + wateringCan.updateItem(player, itemStack, waterInCan - 1, state.getArgs()); + wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); + Collection pots = getPotInRange(potBlock.getLocation(), wateringCan.getWidth(), wateringCan.getLength(), player.getLocation().getYaw(), pot.getKey()); + for (Location location : pots) { + plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(location), wateringCan.getWater()); + pot.trigger(ActionTrigger.ADD_WATER, new State(player, itemStack, location)); + } + } else { + state.setArg("{current}", "0"); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); + wateringCan.trigger(ActionTrigger.NO_WATER, state); + } + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL), + /* + * Handle clicking furniture with a watering can + * This furniture may be a sprinkler, or it may be a custom piece of furniture such as a well + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { + return FunctionResult.PASS; + } + // check watering-can requirements + State state = new State(furnitureWrapper.getPlayer(), furnitureWrapper.getItemInHand(), furnitureWrapper.getLocation()); + if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { + return FunctionResult.RETURN; + } + // get water in can + int waterInCan = wateringCan.getCurrentWater(furnitureWrapper.getItemInHand()); + String clickedFurnitureID = furnitureWrapper.getID(); + Sprinkler sprinkler = getSprinklerBy3DItemID(clickedFurnitureID); + // is a sprinkler + if (sprinkler != null) { + // check sprinkler requirements + if (!RequirementManager.isRequirementMet(state, sprinkler.getUseRequirements())) { + return FunctionResult.RETURN; + } + // check whitelist + if (!wateringCan.getSprinklerWhitelist().contains(sprinkler.getKey())) { + wateringCan.trigger(ActionTrigger.WRONG_SPRINKLER, state); + return FunctionResult.RETURN; + } + // check amount of water + if (waterInCan > 0 || wateringCan.isInfinite()) { + // get sprinkler data + SimpleLocation location = SimpleLocation.of(furnitureWrapper.getLocation()); + Optional worldSprinkler = plugin.getWorldManager().getSprinklerAt(location); + if (worldSprinkler.isEmpty()) { + plugin.debug("Player " + furnitureWrapper.getPlayer().getName() + " tried to interact a sprinkler which not exists in memory. Fixing the data..."); + } else { + if (sprinkler.getStorage() <= worldSprinkler.get().getWater()) { + return FunctionResult.RETURN; + } + } + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + state.setArg("{current}", String.valueOf(waterInCan - 1)); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan - 1, wateringCan.getStorage())); + wateringCan.updateItem(furnitureWrapper.getPlayer(), furnitureWrapper.getItemInHand(), waterInCan - 1, state.getArgs()); + wateringCan.trigger(ActionTrigger.CONSUME_WATER, state); + plugin.getWorldManager().addWaterToSprinkler(sprinkler, location, 1); + } else { + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + state.setArg("{current}", "0"); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(0, wateringCan.getStorage())); + wateringCan.trigger(ActionTrigger.NO_WATER, state); + } + return FunctionResult.RETURN; + } + + // get water from furniture and add it to watering-can + if (!wateringCan.isInfinite()) { + PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); + for (PositiveFillMethod method : methods) { + if (method.getId().equals(clickedFurnitureID)) { + if (method.canFill(state)) { + if (waterInCan < wateringCan.getStorage()) { + waterInCan += method.getAmount(); + waterInCan = Math.min(waterInCan, wateringCan.getStorage()); + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + state.setArg("{current}", String.valueOf(waterInCan)); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(waterInCan, wateringCan.getStorage())); + wateringCan.updateItem(furnitureWrapper.getPlayer(), furnitureWrapper.getItemInHand(), waterInCan, state.getArgs()); + wateringCan.trigger(ActionTrigger.ADD_WATER, state); + method.trigger(state); + } else { + wateringCan.trigger(ActionTrigger.FULL, state); + } + } + return FunctionResult.RETURN; + } + } + } + + return FunctionResult.PASS; + }, CFunction.FunctionPriority.NORMAL), + /* + * Handle clicking a block or air with watering can + * The priority of handling this is the lowest, + * because the priority of watering is much higher than that of adding water, + * and there should be no further judgment on adding water when watering occurs. + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { + return FunctionResult.PASS; + } + if (wateringCan.isInfinite()) { + return FunctionResult.PASS; + } + // get the clicked block + Block targetBlock = interactWrapper.getPlayer().getTargetBlockExact(5, FluidCollisionMode.ALWAYS); + if (targetBlock == null) + return FunctionResult.PASS; + // check watering-can requirements + State state = new State(interactWrapper.getPlayer(), interactWrapper.getItemInHand(), targetBlock.getLocation()); + if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { + return FunctionResult.RETURN; + } + // get the exact block id + String blockID = customProvider.getBlockID(targetBlock); + if (targetBlock.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { + blockID = "WATER"; + } + int water = wateringCan.getCurrentWater(interactWrapper.getItemInHand()); + PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); + for (PositiveFillMethod method : methods) { + if (method.getId().equals(blockID)) { + if (method.canFill(state)) { + if (water < wateringCan.getStorage()) { + water += method.getAmount(); + water = Math.min(water, wateringCan.getStorage()); + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + state.setArg("{current}", String.valueOf(water)); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(water, wateringCan.getStorage())); + wateringCan.updateItem(interactWrapper.getPlayer(), interactWrapper.getItemInHand(), water, state.getArgs()); + wateringCan.trigger(ActionTrigger.ADD_WATER, state); + method.trigger(state); + } else { + wateringCan.trigger(ActionTrigger.FULL, state); + } + } + return FunctionResult.RETURN; + } + } + return FunctionResult.PASS; + }, CFunction.FunctionPriority.LOW) + ); + + this.registerItemFunction(itemID, FunctionTrigger.INTERACT_AIR, + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { + return FunctionResult.PASS; + } + if (wateringCan.isInfinite()) { + return FunctionResult.PASS; + } + // get the clicked block + Block targetBlock = interactWrapper.getPlayer().getTargetBlockExact(5, FluidCollisionMode.ALWAYS); + if (targetBlock == null) + return FunctionResult.PASS; + // check watering-can requirements + State state = new State(interactWrapper.getPlayer(), interactWrapper.getItemInHand(), targetBlock.getLocation()); + if (!RequirementManager.isRequirementMet(state, wateringCan.getRequirements())) { + return FunctionResult.RETURN; + } + // get the exact block id + String blockID = customProvider.getBlockID(targetBlock); + if (targetBlock.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged()) { + blockID = "WATER"; + } + int water = wateringCan.getCurrentWater(interactWrapper.getItemInHand()); + PositiveFillMethod[] methods = wateringCan.getPositiveFillMethods(); + for (PositiveFillMethod method : methods) { + if (method.getId().equals(blockID)) { + if (method.canFill(state)) { + if (water < wateringCan.getStorage()) { + water += method.getAmount(); + water = Math.min(water, wateringCan.getStorage()); + state.setArg("{storage}", String.valueOf(wateringCan.getStorage())); + state.setArg("{current}", String.valueOf(water)); + state.setArg("{water_bar}", wateringCan.getWaterBar() == null ? "" : wateringCan.getWaterBar().getWaterBar(water, wateringCan.getStorage())); + wateringCan.updateItem(interactWrapper.getPlayer(), interactWrapper.getItemInHand(), water, state.getArgs()); + wateringCan.trigger(ActionTrigger.ADD_WATER, state); + method.trigger(state); + } else { + wateringCan.trigger(ActionTrigger.FULL, state); + } + } + return FunctionResult.RETURN; + } + } + return FunctionResult.PASS; + }, CFunction.FunctionPriority.NORMAL) + ); + } + + @SuppressWarnings("DuplicatedCode") + private void loadSprinkler(String key, ConfigurationSection section) { + int storage = section.getInt("storage", 4); + boolean infinite = section.getBoolean("infinite", false); + int range = section.getInt("range",1); + int water = section.getInt("water", 1); + ItemCarrier itemCarrier = ItemCarrier.valueOf(section.getString("type", "ITEM_FRAME").toUpperCase(Locale.ENGLISH)); + + SprinklerConfig sprinkler = new SprinklerConfig( + key, + itemCarrier, + section.getString("2D-item"), + Preconditions.checkNotNull(section.getString("3D-item"), "3D-item can't be null"), + section.getString("3D-item-with-water"), + range, + storage, + water, + infinite, + section.contains("water-bar") ? WaterBar.of( + section.getString("water-bar.left", ""), + section.getString("water-bar.empty", ""), + section.getString("water-bar.full", ""), + section.getString("water-bar.right", "") + ) : null, + new HashSet<>(section.getStringList("pot-whitelist")), + ConfigUtils.getPassiveFillMethods(section.getConfigurationSection("fill-method")), + ConfigUtils.getActionMap(section.getConfigurationSection("events")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.place")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.break")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.use")) + ); + + if (!this.registerSprinkler(sprinkler)) { + LogUtils.warn("Failed to register new sprinkler: " + key + " due to duplicated entries."); + return; + } + + if (sprinkler.get2DItemID() != null) { + this.registerItemFunction(sprinkler.get2DItemID(), FunctionTrigger.INTERACT_AT, + /* + * 2D item -> 3D item + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { + return FunctionResult.PASS; + } + if (interactBlockWrapper.getClickedFace() != BlockFace.UP) { + return FunctionResult.PASS; + } + if (!interactBlockWrapper.getClickedBlock().getType().isSolid()) { + return FunctionResult.PASS; + } + ItemStack itemInHand = interactBlockWrapper.getItemInHand(); + Location placed = interactBlockWrapper.getClickedBlock().getLocation().clone().add(0,1,0); + Player player = interactBlockWrapper.getPlayer(); + // check if the place is empty + if (!customProvider.isAir(placed)) { + return FunctionResult.RETURN; + } + // check place requirements + State state = new State(player, itemInHand, placed); + if (!RequirementManager.isRequirementMet(state, sprinkler.getPlaceRequirements())) { + return FunctionResult.RETURN; + } + // check limitation + SimpleLocation simpleLocation = SimpleLocation.of(placed); + if (plugin.getWorldManager().isReachLimit(simpleLocation, ItemType.SPRINKLER)) { + sprinkler.trigger(ActionTrigger.REACH_LIMIT, state); + return FunctionResult.RETURN; + } + // fire event + SprinklerPlaceEvent placeEvent = new SprinklerPlaceEvent(player, itemInHand, placed, sprinkler); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + return FunctionResult.RETURN; + } + // place the sprinkler + switch (sprinkler.getItemCarrier()) { + case ITEM_FRAME, ITEM_DISPLAY -> customProvider.placeFurniture(placed, sprinkler.get3DItemID()); + case TRIPWIRE -> customProvider.placeBlock(placed, sprinkler.get3DItemID()); + default -> { + LogUtils.warn("Unsupported type for sprinkler: " + sprinkler.getItemCarrier().name()); + return FunctionResult.RETURN; + } + } + // reduce item + if (player.getGameMode() != GameMode.CREATIVE) + itemInHand.setAmount(itemInHand.getAmount() - 1); + sprinkler.trigger(ActionTrigger.PLACE, state); + plugin.getWorldManager().addSprinklerAt(new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0), simpleLocation); + return FunctionResult.PASS; + }, CFunction.FunctionPriority.NORMAL) + ); + } + + this.registerItemFunction(sprinkler.get3DItemID(), FunctionTrigger.PLACE, + /* + * This will only trigger if the sprinkler has only 3D items + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof PlaceFurnitureWrapper placeFurnitureWrapper)) { + return FunctionResult.PASS; + } + Location location = placeFurnitureWrapper.getLocation(); + Player player = placeFurnitureWrapper.getPlayer(); + // check place requirements + State state = new State(player, placeFurnitureWrapper.getItemInHand(), location); + if (!RequirementManager.isRequirementMet(state, sprinkler.getPlaceRequirements())) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // check limitation + SimpleLocation simpleLocation = SimpleLocation.of(location); + if (plugin.getWorldManager().isReachLimit(simpleLocation, ItemType.SPRINKLER)) { + sprinkler.trigger(ActionTrigger.REACH_LIMIT, state); + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // fire event + SprinklerPlaceEvent placeEvent = new SprinklerPlaceEvent(player, placeFurnitureWrapper.getItemInHand(), location, sprinkler); + if (EventUtils.fireAndCheckCancel(placeEvent)) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // add data + plugin.getWorldManager().addSprinklerAt(new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0), simpleLocation); + sprinkler.trigger(ActionTrigger.PLACE, state); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + + this.registerItemFunction(sprinkler.get3DItemID(), FunctionTrigger.BE_INTERACTED, + /* + * Interact the sprinkler + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractFurnitureWrapper interactFurnitureWrapper)) { + return FunctionResult.PASS; + } + ItemStack itemInHand = interactFurnitureWrapper.getItemInHand(); + Player player = interactFurnitureWrapper.getPlayer(); + Location location = interactFurnitureWrapper.getLocation(); + // check use requirements + State state = new State(player, itemInHand, location); + if (!RequirementManager.isRequirementMet(state, sprinkler.getUseRequirements())) { + return FunctionResult.RETURN; + } + SimpleLocation simpleLocation = SimpleLocation.of(location); + Optional optionalSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation); + if (optionalSprinkler.isEmpty()) { + plugin.debug("Found a sprinkler without data interacted by " + player.getName() + " at " + location); + plugin.getWorldManager().addSprinklerAt(new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0), simpleLocation); + return FunctionResult.RETURN; + } + if (!optionalSprinkler.get().getKey().equals(sprinkler.getKey())) { + LogUtils.warn("Found a sprinkler having inconsistent data interacted by " + player.getName() + " at " + location + "."); + plugin.getWorldManager().addSprinklerAt(new MemorySprinkler(simpleLocation, sprinkler.getKey(), 0), simpleLocation); + return FunctionResult.RETURN; + } + // fire the event + SprinklerInteractEvent interactEvent = new SprinklerInteractEvent(player, itemInHand, location, optionalSprinkler.get()); + if (EventUtils.fireAndCheckCancel(interactEvent)) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // add water to sprinkler + String itemID = getItemID(itemInHand); + int itemAmount = itemInHand.getAmount(); + Optional worldSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation); + int waterInSprinkler = worldSprinkler.map(WorldSprinkler::getWater).orElse(0); + // if it's not infinite + if (!sprinkler.isInfinite()) { + for (PassiveFillMethod method : sprinkler.getPassiveFillMethods()) { + if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { + if (method.canFill(state)) { + if (waterInSprinkler < sprinkler.getStorage()) { + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemAmount - method.getUsedAmount()); + if (method.getReturned() != null) { + ItemStack returned = getItemStack(player, method.getReturned()); + ItemUtils.giveItem(player, returned, method.getReturnedAmount()); + } + } + int current = Math.min(waterInSprinkler + method.getAmount(), sprinkler.getStorage()); + state.setArg("{storage}", String.valueOf(sprinkler.getStorage())); + state.setArg("{current}", String.valueOf(current)); + state.setArg("{water_bar}", sprinkler.getWaterBar() == null ? "" : sprinkler.getWaterBar().getWaterBar(current, sprinkler.getStorage())); + method.trigger(state); + sprinkler.trigger(ActionTrigger.ADD_WATER, state); + plugin.getWorldManager().addWaterToSprinkler(sprinkler, simpleLocation, method.getAmount()); + } else { + state.setArg("{storage}", String.valueOf(sprinkler.getStorage())); + state.setArg("{current}", String.valueOf(sprinkler.getStorage())); + state.setArg("{water_bar}", sprinkler.getWaterBar() == null ? "" : sprinkler.getWaterBar().getWaterBar(sprinkler.getStorage(), sprinkler.getStorage())); + sprinkler.trigger(ActionTrigger.FULL, state); + } + } + return FunctionResult.RETURN; + } + } + } + + return FunctionResult.PASS; + }, CFunction.FunctionPriority.NORMAL) + ); + + this.registerItemFunction(sprinkler.get3DItemID(), FunctionTrigger.BE_INTERACTED, + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractFurnitureWrapper interactFurnitureWrapper)) { + return FunctionResult.PASS; + } + + Location location = interactFurnitureWrapper.getLocation(); + // trigger interact actions + plugin.getScheduler().runTaskSyncLater(() -> { + State state = new State(interactFurnitureWrapper.getPlayer(), interactFurnitureWrapper.getItemInHand(), location); + Optional optionalSprinkler = plugin.getWorldManager().getSprinklerAt(SimpleLocation.of(location)); + if (optionalSprinkler.isEmpty()) { + return; + } + + state.setArg("{storage}", String.valueOf(sprinkler.getStorage())); + state.setArg("{current}", String.valueOf(optionalSprinkler.get().getWater())); + state.setArg("{water_bar}", sprinkler.getWaterBar() == null ? "" : sprinkler.getWaterBar().getWaterBar(optionalSprinkler.get().getWater(), sprinkler.getStorage())); + + sprinkler.trigger(ActionTrigger.INTERACT, state); + }, location, 1); + + return FunctionResult.PASS; + }, CFunction.FunctionPriority.LOWEST) + ); + + this.registerItemFunction(sprinkler.get3DItemID(), FunctionTrigger.BREAK, + /* + * Handle breaking sprinklers + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof BreakFurnitureWrapper breakFurnitureWrapper)) { + return FunctionResult.PASS; + } + // check break requirements + Location location = breakFurnitureWrapper.getLocation(); + State state = new State(breakFurnitureWrapper.getPlayer(), breakFurnitureWrapper.getItemInHand(), location); + if (!RequirementManager.isRequirementMet(state, sprinkler.getBreakRequirements())) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + SimpleLocation simpleLocation = SimpleLocation.of(location); + Optional optionalSprinkler = plugin.getWorldManager().getSprinklerAt(simpleLocation); + if (optionalSprinkler.isEmpty()) { + plugin.debug("Found a sprinkler without data broken by " + state.getPlayer().getName() + " at " + location); + return FunctionResult.RETURN; + } + if (!optionalSprinkler.get().getKey().equals(sprinkler.getKey())) { + LogUtils.warn("Found a sprinkler having inconsistent data broken by " + state.getPlayer().getName() + " at " + location + "."); + plugin.getWorldManager().removeSprinklerAt(simpleLocation); + return FunctionResult.RETURN; + } + // fire event + SprinklerBreakEvent breakEvent = new SprinklerBreakEvent(breakFurnitureWrapper.getPlayer(), location, optionalSprinkler.get()); + if (EventUtils.fireAndCheckCancel(breakEvent)) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // remove data + plugin.getWorldManager().removeSprinklerAt(simpleLocation); + sprinkler.trigger(ActionTrigger.BREAK, state); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + } + + @SuppressWarnings("DuplicatedCode") + private void loadFertilizer(String key, ConfigurationSection section) { + FertilizerType type = switch (Preconditions.checkNotNull(section.getString("type"), "Fertilizer type can't be null").toUpperCase(Locale.ENGLISH)) { + case "QUALITY" -> FertilizerType.QUALITY; + case "SOIL_RETAIN" -> FertilizerType.SOIL_RETAIN; + case "SPEED_GROW" -> FertilizerType.SPEED_GROW; + case "VARIATION" -> FertilizerType.VARIATION; + case "YIELD_INCREASE" -> FertilizerType.YIELD_INCREASE; + default -> null; + }; + + if (type == null) { + LogUtils.warn("Fertilizer type: " + section.getString("type") + " is invalid."); + return; + } + + String icon = section.getString("icon", ""); + int times = section.getInt("times", 14); + String itemID = section.getString("item"); + HashSet potWhitelist = new HashSet<>(section.getStringList("pot-whitelist")); + boolean beforePlant = section.getBoolean("before-plant", false); + + Fertilizer fertilizer; + switch (type) { + case QUALITY -> fertilizer = new QualityCropConfig( + key, itemID, times, + section.getDouble("chance", 1), type, potWhitelist, + beforePlant, icon, + ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), + ConfigUtils.getQualityRatio(Preconditions.checkNotNull(section.getString("ratio"), "Quality ratio should not be null")), + ConfigUtils.getActionMap(section.getConfigurationSection("events")) + ); + case VARIATION -> fertilizer = new VariationConfig(key, itemID, times, + section.getDouble("chance", 1), type, potWhitelist, + beforePlant, icon, + ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), + ConfigUtils.getActionMap(section.getConfigurationSection("events")) + ); + case SOIL_RETAIN -> fertilizer = new SoilRetainConfig(key, itemID, times, + section.getDouble("chance", 1), type, potWhitelist, + beforePlant, icon, + ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), + ConfigUtils.getActionMap(section.getConfigurationSection("events")) + ); + case YIELD_INCREASE -> fertilizer = new YieldIncreaseConfig(key, itemID, times, + type, potWhitelist, + beforePlant, icon, + ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), + ConfigUtils.getIntChancePair(section.getConfigurationSection("chance")), + ConfigUtils.getActionMap(section.getConfigurationSection("events")) + ); + case SPEED_GROW -> fertilizer = new SpeedGrowConfig(key, itemID, times, + type, potWhitelist, + beforePlant, icon, + ConfigUtils.getRequirements(section.getConfigurationSection("requirements")), + ConfigUtils.getIntChancePair(section.getConfigurationSection("chance")), + ConfigUtils.getActionMap(section.getConfigurationSection("events")) + ); + default -> fertilizer = null; + } + + if (!registerFertilizer(fertilizer)) { + LogUtils.warn("Failed to register new fertilizer: " + key + " due to duplicated entries."); + return; + } + + this.registerItemFunction(fertilizer.getItemID(), FunctionTrigger.INTERACT_AT, + /* + * Processing logic for players to use fertilizer + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { + return FunctionResult.PASS; + } + // is a pot + Block clicked = interactBlockWrapper.getClickedBlock(); + Pot pot = getPotByBlock(clicked); + if (pot == null) { + return FunctionResult.PASS; + } + ItemStack itemInHand = interactBlockWrapper.getItemInHand(); + Location location = clicked.getLocation(); + // check fertilizer requirements + State state = new State(interactBlockWrapper.getPlayer(), itemInHand, location); + if (!RequirementManager.isRequirementMet(state, fertilizer.getRequirements())) { + return FunctionResult.RETURN; + } + // check pot use requirements + if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { + return FunctionResult.RETURN; + } + // check whitelist + if (!fertilizer.getPotWhitelist().contains(pot.getKey())) { + fertilizer.trigger(ActionTrigger.WRONG_POT, state); + return FunctionResult.RETURN; + } + // check before plant + if (fertilizer.isBeforePlant()) { + Optional worldCrop = plugin.getWorldManager().getCropAt(SimpleLocation.of(location.clone().add(0,1,0))); + if (worldCrop.isPresent()) { + fertilizer.trigger(ActionTrigger.BEFORE_PLANT, state); + return FunctionResult.RETURN; + } + } + SimpleLocation simpleLocation = SimpleLocation.of(location); + Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); + boolean hasWater = false; + if (worldPot.isEmpty()) { + plugin.debug("Found pot data not exists at " + simpleLocation); + } else { + hasWater = worldPot.get().getWater() > 0; + } + + // add data + plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); + updatePotState(location, pot, hasWater, fertilizer); + if (interactBlockWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemInHand.getAmount() - 1); + } + fertilizer.trigger(ActionTrigger.USE, state); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL), + + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { + return FunctionResult.PASS; + } + // is a crop + Block clicked = interactBlockWrapper.getClickedBlock(); + String id = customProvider.getBlockID(clicked); + Crop crop = getCropByStageID(id); + if (crop == null && !deadCrops.contains(id)) { + return FunctionResult.PASS; + } + ItemStack itemInHand = interactBlockWrapper.getItemInHand(); + Location location = clicked.getLocation(); + Player player = interactBlockWrapper.getPlayer(); + Location potLocation = location.clone().subtract(0,1,0); + // check fertilizer requirements + State state = new State(player, itemInHand, potLocation); + if (!RequirementManager.isRequirementMet(state, fertilizer.getRequirements())) { + return FunctionResult.RETURN; + } + // check pot data + Pot pot = getPotByBlock(potLocation.getBlock()); + if (pot == null) { + LogUtils.warn("Found a crop without pot interacted by player " + player.getName() + " with a fertilizer at " + location); + customProvider.removeAnythingAt(location); + return FunctionResult.RETURN; + } + // check pot use requirements + if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { + return FunctionResult.RETURN; + } + // check whitelist + if (!fertilizer.getPotWhitelist().contains(pot.getKey())) { + fertilizer.trigger(ActionTrigger.WRONG_POT, state); + return FunctionResult.RETURN; + } + // check before plant + if (fertilizer.isBeforePlant()) { + fertilizer.trigger(ActionTrigger.BEFORE_PLANT, state); + return FunctionResult.RETURN; + } + + SimpleLocation simpleLocation = SimpleLocation.of(potLocation); + Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); + boolean hasWater = false; + if (worldPot.isEmpty()) { + plugin.debug("Found pot data not exists at " + potLocation); + } else { + hasWater = worldPot.get().getWater() > 0; + } + + // add data + plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); + updatePotState(potLocation, pot, hasWater, fertilizer); + if (interactBlockWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemInHand.getAmount() - 1); + } + fertilizer.trigger(ActionTrigger.USE, state); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL), + + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractFurnitureWrapper furnitureWrapper)) { + return FunctionResult.PASS; + } + // is a crop + String id = furnitureWrapper.getID(); + Crop crop = getCropByStageID(id); + if (crop == null && !deadCrops.contains(id)) { + return FunctionResult.PASS; + } + ItemStack itemInHand = furnitureWrapper.getItemInHand(); + Location location = furnitureWrapper.getLocation(); + Player player = furnitureWrapper.getPlayer(); + Location potLocation = location.clone().subtract(0,1,0); + // check fertilizer requirements + State state = new State(player, itemInHand, potLocation); + if (!RequirementManager.isRequirementMet(state, fertilizer.getRequirements())) { + return FunctionResult.RETURN; + } + // check pot data + Pot pot = getPotByBlock(potLocation.getBlock()); + if (pot == null) { + LogUtils.warn("Found a crop without pot interacted by player " + player.getName() + " with a fertilizer at " + location); + customProvider.removeAnythingAt(location); + return FunctionResult.RETURN; + } + // check pot use requirements + if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { + return FunctionResult.RETURN; + } + // check whitelist + if (!fertilizer.getPotWhitelist().contains(pot.getKey())) { + fertilizer.trigger(ActionTrigger.WRONG_POT, state); + return FunctionResult.RETURN; + } + // check before plant + if (fertilizer.isBeforePlant()) { + fertilizer.trigger(ActionTrigger.BEFORE_PLANT, state); + return FunctionResult.RETURN; + } + + SimpleLocation simpleLocation = SimpleLocation.of(potLocation); + Optional worldPot = plugin.getWorldManager().getPotAt(simpleLocation); + boolean hasWater = false; + if (worldPot.isEmpty()) { + plugin.debug("Found pot data not exists at " + potLocation); + } else { + hasWater = worldPot.get().getWater() > 0; + } + + // add data + plugin.getWorldManager().addFertilizerToPot(pot, fertilizer, simpleLocation); + updatePotState(potLocation, pot, hasWater, fertilizer); + if (furnitureWrapper.getPlayer().getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemInHand.getAmount() - 1); + } + fertilizer.trigger(ActionTrigger.USE, state); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + } + + @SuppressWarnings("DuplicatedCode") + private void loadCrop(String key, ConfigurationSection section) { + ItemCarrier itemCarrier = ItemCarrier.valueOf(section.getString("type")); + if (itemCarrier != ItemCarrier.TRIPWIRE && itemCarrier != ItemCarrier.ITEM_DISPLAY && itemCarrier != ItemCarrier.ITEM_FRAME) { + LogUtils.warn("Unsupported crop type: " + itemCarrier.name()); + return; + } + + String seedItemID = section.getString("seed"); + boolean rotation = section.getBoolean("random-rotation", false); + int maxPoints = section.getInt("max-points"); + + ConfigurationSection pointSection = section.getConfigurationSection("points"); + if (pointSection == null) { + LogUtils.warn(key + ".points section can't be null"); + return; + } + + CropConfig crop = new CropConfig( + key, seedItemID, itemCarrier, + new HashSet<>(section.getStringList("pot-whitelist")), rotation, maxPoints, + ConfigUtils.getBoneMeals(section.getConfigurationSection("custom-bone-meal")), + new Conditions(ConfigUtils.getConditions(section.getConfigurationSection("grow-conditions"))), + ConfigUtils.getDeathConditions(section.getConfigurationSection("death-conditions"), itemCarrier), + ConfigUtils.getActionMap(section.getConfigurationSection("events")), + ConfigUtils.getStageConfigs(pointSection), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.plant")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.break")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.interact")) + ); + + if (!this.registerCrop(crop)) { + LogUtils.warn("Failed to register new crop: " + key + " due to duplicated entries."); + return; + } + + for (DeathConditions deathConditions : crop.getDeathConditions()) { + if (deathConditions.getDeathItem() != null) { + deadCrops.add(deathConditions.getDeathItem()); + } + } + + this.registerItemFunction(crop.getSeedItemID(), FunctionTrigger.INTERACT_AT, + /* + * Handle crop planting + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper blockWrapper)) { + return FunctionResult.PASS; + } + // is a pot + Block clicked = blockWrapper.getClickedBlock(); + Pot pot = getPotByBlock(clicked); + if (pot == null) { + return FunctionResult.PASS; + } + // click the upper face + if (blockWrapper.getClickedFace() != BlockFace.UP) { + return FunctionResult.PASS; + } + if (!customProvider.isAir(clicked.getRelative(BlockFace.UP).getLocation())) { + return FunctionResult.RETURN; + } + Player player = blockWrapper.getPlayer(); + ItemStack itemInHand = blockWrapper.getItemInHand(); + Location seedLocation = clicked.getLocation().clone().add(0,1,0); + State state = new State(player, itemInHand, seedLocation); + // check whitelist + if (!crop.getPotWhitelist().contains(pot.getKey())) { + crop.trigger(ActionTrigger.WRONG_POT, state); + return FunctionResult.RETURN; + } + // check plant requirements + if (!RequirementManager.isRequirementMet(state, crop.getPlantRequirements())) { + return FunctionResult.RETURN; + } + // check limitation + SimpleLocation simpleLocation = SimpleLocation.of(seedLocation); + if (plugin.getWorldManager().isReachLimit(simpleLocation, ItemType.CROP)) { + crop.trigger(ActionTrigger.REACH_LIMIT, state); + return FunctionResult.RETURN; + } + // fire event + CropPlantEvent plantEvent = new CropPlantEvent(player, itemInHand, seedLocation, crop, 0); + if (EventUtils.fireAndCheckCancel(plantEvent)) { + return FunctionResult.RETURN; + } + // place the crop + switch (crop.getItemCarrier()) { + case ITEM_FRAME, ITEM_DISPLAY -> customProvider.placeFurniture(seedLocation, crop.getStageItemByPoint(plantEvent.getPoint())); + case TRIPWIRE -> customProvider.placeBlock(seedLocation, crop.getStageItemByPoint(plantEvent.getPoint())); + default -> { + LogUtils.warn("Unsupported type for crop: " + crop.getItemCarrier().name()); + return FunctionResult.RETURN; + } + } + // reduce item + if (player.getGameMode() != GameMode.CREATIVE) + itemInHand.setAmount(itemInHand.getAmount() - 1); + crop.trigger(ActionTrigger.PLANT, state); + + plugin.getWorldManager().addCropAt(new MemoryCrop(simpleLocation, crop.getKey(), plantEvent.getPoint()), simpleLocation); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + + for (Crop.Stage stage : crop.getStages()) { + if (stage.getStageID() != null) { + this.registerItemFunction(stage.getStageID(), FunctionTrigger.BE_INTERACTED, + /* + * Add water to pot if player is clicking a crop + * Trigger crop interaction + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractWrapper interactWrapper)) { + return FunctionResult.PASS; + } + Location cropLocation = interactWrapper.getLocation().toBlockLocation(); + Player player = interactWrapper.getPlayer(); + ItemStack itemInHand = interactWrapper.getItemInHand(); + State cropState = new State(player, itemInHand, cropLocation); + + // check crop interact requirements + if (!RequirementManager.isRequirementMet(cropState, crop.getInteractRequirements())) { + return FunctionResult.RETURN; + } + if (!RequirementManager.isRequirementMet(cropState, stage.getInteractRequirements())) { + return FunctionResult.RETURN; + } + SimpleLocation simpleLocation = SimpleLocation.of(cropLocation); + Optional optionalCrop = plugin.getWorldManager().getCropAt(simpleLocation); + if (optionalCrop.isEmpty()) { + plugin.debug("Found a crop without data interacted by " + player.getName() + " at " + cropLocation); + plugin.getWorldManager().addCropAt(new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint()), simpleLocation); + } else { + if (!optionalCrop.get().getKey().equals(crop.getKey())) { + LogUtils.warn("Found a crop having inconsistent data interacted by " + player.getName() + " at " + cropLocation + "."); + plugin.getWorldManager().addCropAt(new MemoryCrop(simpleLocation, crop.getKey(), stage.getPoint()), simpleLocation); + return FunctionResult.RETURN; + } + } + + CropInteractEvent interactEvent = new CropInteractEvent(conditionWrapper.getPlayer(), interactWrapper.getItemInHand(), cropLocation, optionalCrop.orElse(null)); + if (EventUtils.fireAndCheckCancel(interactEvent)) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + + String itemID = getItemID(itemInHand); + int itemAmount = itemInHand.getAmount(); + + Location potLocation = cropLocation.clone().subtract(0,1,0); + Pot pot = getPotByBlock(potLocation.getBlock()); + State potState = new State(player, itemInHand, potLocation); + + // check pot use requirements + if (pot != null && RequirementManager.isRequirementMet(potState, pot.getUseRequirements())) { + // get water in pot + int waterInPot = plugin.getWorldManager().getPotAt(SimpleLocation.of(potLocation)).map(WorldPot::getWater).orElse(0); + // water the pot + for (PassiveFillMethod method : pot.getPassiveFillMethods()) { + if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { + if (method.canFill(potState)) { + if (waterInPot < pot.getStorage()) { + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemAmount - method.getUsedAmount()); + if (method.getReturned() != null) { + ItemStack returned = getItemStack(player, method.getReturned()); + ItemUtils.giveItem(player, returned, method.getReturnedAmount()); + } + } + method.trigger(potState); + pot.trigger(ActionTrigger.ADD_WATER, potState); + plugin.getWorldManager().addWaterToPot(pot, SimpleLocation.of(potLocation), method.getAmount()); + } else { + pot.trigger(ActionTrigger.FULL, potState); + } + } + return FunctionResult.RETURN; + } + } + } + + // if not reached the max point, try detecting bone meals + if (optionalCrop.isPresent()) { + if (optionalCrop.get().getPoint() < crop.getMaxPoints()) { + for (BoneMeal boneMeal : crop.getBoneMeals()) { + if (boneMeal.getItem().equals(itemID)) { + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemAmount - boneMeal.getUsedAmount()); + if (boneMeal.getReturned() != null) { + ItemStack returned = getItemStack(player, boneMeal.getReturned()); + ItemUtils.giveItem(player, returned, boneMeal.getReturnedAmount()); + } + } + boneMeal.trigger(cropState); + plugin.getWorldManager().addPointToCrop(crop, SimpleLocation.of(cropLocation), boneMeal.getPoint()); + return FunctionResult.RETURN; + } + } + } else { + crop.trigger(ActionTrigger.RIPE, cropState); + } + } + + // trigger interact actions + crop.trigger(ActionTrigger.INTERACT, cropState); + stage.trigger(ActionTrigger.INTERACT, cropState); + return FunctionResult.PASS; + }, CFunction.FunctionPriority.HIGH) + ); + + this.registerItemFunction(stage.getStageID(), FunctionTrigger.BREAK, + /* + * Break the crop + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof BreakWrapper breakWrapper)) { + return FunctionResult.PASS; + } + Player player = breakWrapper.getPlayer(); + Location cropLocation = breakWrapper.getLocation().toBlockLocation(); + State state = new State(player, breakWrapper.getItemInHand(), cropLocation); + // check crop break requirements + if (!RequirementManager.isRequirementMet(state, crop.getBreakRequirements())) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + if (!RequirementManager.isRequirementMet(state, stage.getBreakRequirements())) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + SimpleLocation simpleLocation = SimpleLocation.of(cropLocation); + Optional optionalWorldCrop = plugin.getWorldManager().getCropAt(simpleLocation); + if (optionalWorldCrop.isEmpty()) { + plugin.debug("Found a crop without data broken by " + player.getName() + " at " + cropLocation + ". " + + "You can safely ignore this if the crop is spawned in the wild."); + } else { + if (!optionalWorldCrop.get().getKey().equals(crop.getKey())) { + LogUtils.warn("Found a crop having inconsistent data broken by " + player.getName() + " at " + cropLocation + "."); + plugin.getWorldManager().removeCropAt(simpleLocation); + return FunctionResult.RETURN; + } + } + // fire event + CropBreakEvent breakEvent = new CropBreakEvent(player, cropLocation, optionalWorldCrop.orElse(null)); + if (EventUtils.fireAndCheckCancel(breakEvent)) + return FunctionResult.CANCEL_EVENT_AND_RETURN; + // trigger actions + stage.trigger(ActionTrigger.BREAK, state); + crop.trigger(ActionTrigger.BREAK, state); + plugin.getWorldManager().removeCropAt(simpleLocation); + return FunctionResult.PASS; + }, CFunction.FunctionPriority.NORMAL) + ); + } + } + } + + @SuppressWarnings("DuplicatedCode") + private void loadPot(String key, ConfigurationSection section) { + int storage = section.getInt("max-water-storage", 1); + String dryModel = Preconditions.checkNotNull(section.getString("base.dry"), "base.dry should not be null"); + String wetModel = Preconditions.checkNotNull(section.getString("base.wet"), "base.wet should not be null"); + boolean enableFertilizedAppearance = section.getBoolean("fertilized-pots.enable", false); + + PotConfig pot = new PotConfig( + key, storage, section.getBoolean("absorb-rainwater", true), section.getBoolean("absorb-nearby-water", false), + dryModel, wetModel, + enableFertilizedAppearance, + enableFertilizedAppearance ? ConfigUtils.getFertilizedPotMap(section.getConfigurationSection("fertilized-pots")) : new HashMap<>(), + section.contains("water-bar") ? WaterBar.of( + section.getString("water-bar.left", ""), + section.getString("water-bar.empty", ""), + section.getString("water-bar.full", ""), + section.getString("water-bar.right", "") + ) : null, + ConfigUtils.getPassiveFillMethods(section.getConfigurationSection("fill-method")), + ConfigUtils.getActionMap(section.getConfigurationSection("events")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.place")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.break")), + ConfigUtils.getRequirements(section.getConfigurationSection("requirements.use")) + ); + + if (!this.registerPot(pot)) { + LogUtils.warn("Failed to register new pot: " + key + " due to duplicated entries."); + return; + } + + for (String potItemID : pot.getPotBlocks()) { + this.registerItemFunction(potItemID, FunctionTrigger.BE_INTERACTED, + /* + * Interact the pot + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { + return FunctionResult.PASS; + } + + ItemStack itemInHand = interactBlockWrapper.getItemInHand(); + Player player = interactBlockWrapper.getPlayer(); + Location location = interactBlockWrapper.getClickedBlock().getLocation(); + // check pot use requirement + State state = new State(player, itemInHand, location); + if (!RequirementManager.isRequirementMet(state, pot.getUseRequirements())) { + return FunctionResult.RETURN; + } + SimpleLocation simpleLocation = SimpleLocation.of(location); + Optional optionalPot = plugin.getWorldManager().getPotAt(simpleLocation); + if (optionalPot.isEmpty()) { + plugin.debug("Found a pot without data interacted by " + player.getName() + " at " + location); + plugin.getWorldManager().addPotAt(new MemoryPot(simpleLocation, pot.getKey()), simpleLocation); + return FunctionResult.RETURN; + } + if (!optionalPot.get().getKey().equals(pot.getKey())) { + LogUtils.warn("Found a pot having inconsistent data interacted by " + player.getName() + " at " + location + "."); + plugin.getWorldManager().addPotAt(new MemoryPot(simpleLocation, pot.getKey()), simpleLocation); + return FunctionResult.RETURN; + } + // fire the event + PotInteractEvent interactEvent = new PotInteractEvent(player, itemInHand, location, optionalPot.get()); + if (EventUtils.fireAndCheckCancel(interactEvent)) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + String itemID = getItemID(itemInHand); + int itemAmount = itemInHand.getAmount(); + // get water in pot + int waterInPot = plugin.getWorldManager().getPotAt(simpleLocation).map(WorldPot::getWater).orElse(0); + for (PassiveFillMethod method : pot.getPassiveFillMethods()) { + if (method.getUsed().equals(itemID) && itemAmount >= method.getUsedAmount()) { + if (method.canFill(state)) { + if (waterInPot < pot.getStorage()) { + if (player.getGameMode() != GameMode.CREATIVE) { + itemInHand.setAmount(itemAmount - method.getUsedAmount()); + if (method.getReturned() != null) { + ItemStack returned = getItemStack(player, method.getReturned()); + ItemUtils.giveItem(player, returned, method.getReturnedAmount()); + } + } + method.trigger(state); + pot.trigger(ActionTrigger.ADD_WATER, state); + plugin.getWorldManager().addWaterToPot(pot, simpleLocation, method.getAmount()); + } else { + pot.trigger(ActionTrigger.FULL, state); + } + } + return FunctionResult.RETURN; + } + } + + return FunctionResult.PASS; + }, CFunction.FunctionPriority.NORMAL) + ); + + this.registerItemFunction(potItemID, FunctionTrigger.BE_INTERACTED, + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof InteractBlockWrapper interactBlockWrapper)) { + return FunctionResult.PASS; + } + + Location location = interactBlockWrapper.getClickedBlock().getLocation(); + plugin.getScheduler().runTaskSyncLater(() -> { + Optional worldPot = plugin.getWorldManager().getPotAt(SimpleLocation.of(location)); + if (worldPot.isEmpty()) { + return; + } + State state = new State(interactBlockWrapper.getPlayer(), interactBlockWrapper.getItemInHand(), location); + state.setArg("{current}", String.valueOf(worldPot.get().getWater())); + state.setArg("{storage}", String.valueOf(pot.getStorage())); + state.setArg("{water_bar}", pot.getWaterBar() == null ? "" : pot.getWaterBar().getWaterBar(worldPot.get().getWater(), pot.getStorage())); + state.setArg("{left_times}", String.valueOf(worldPot.get().getFertilizerTimes())); + state.setArg("{max_times}", String.valueOf(Optional.ofNullable(worldPot.get().getFertilizer()).map(fertilizer -> fertilizer.getTimes()).orElse(0))); + state.setArg("{icon}", Optional.ofNullable(worldPot.get().getFertilizer()).map(fertilizer -> fertilizer.getIcon()).orElse("")); + + // trigger actions + pot.trigger(ActionTrigger.INTERACT, state); + }, location, 1); + return FunctionResult.PASS; + }, CFunction.FunctionPriority.LOWEST) + ); + + this.registerItemFunction(potItemID, FunctionTrigger.BREAK, + /* + * Break the pot + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof BreakBlockWrapper blockWrapper)) { + return FunctionResult.PASS; + } + // check break requirements + Location location = blockWrapper.getBrokenBlock().getLocation(); + Player player = blockWrapper.getPlayer(); + State state = new State(player, blockWrapper.getItemInHand(), location); + if (!RequirementManager.isRequirementMet(state, pot.getBreakRequirements())) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + Location cropLocation = location.clone().add(0,1,0); + String cropStageID = customProvider.getSomethingAt(cropLocation); + // remove crops + Crop.Stage stage = stage2CropStageMap.get(cropStageID); + if (stage != null) { + // if crops are above, check the break requirements for crops + Crop crop = getCropByStageID(cropStageID); + if (crop != null) { + State cropState = new State(player, blockWrapper.getItemInHand(), cropLocation); + if (!RequirementManager.isRequirementMet(cropState, crop.getBreakRequirements())) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + if (!RequirementManager.isRequirementMet(cropState, stage.getBreakRequirements())) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + Optional optionalWorldCrop = plugin.getWorldManager().getCropAt(SimpleLocation.of(cropLocation)); + if (optionalWorldCrop.isPresent()) { + if (!optionalWorldCrop.get().getKey().equals(crop.getKey())) { + LogUtils.warn("Found a crop having inconsistent data broken by " + player.getName() + " at " + cropLocation + "."); + } + } else { + plugin.debug("Found a crop without data broken by " + player.getName() + " at " + cropLocation + ". " + + "You can safely ignore this if the crop is spawned in the wild."); + } + // fire event + CropBreakEvent breakEvent = new CropBreakEvent(player, cropLocation, optionalWorldCrop.orElse(null)); + if (EventUtils.fireAndCheckCancel(breakEvent)) + return FunctionResult.CANCEL_EVENT_AND_RETURN; + // trigger actions + stage.trigger(ActionTrigger.BREAK, cropState); + crop.trigger(ActionTrigger.BREAK, cropState); + plugin.getWorldManager().removeCropAt(SimpleLocation.of(cropLocation)); + customProvider.removeAnythingAt(cropLocation); + } else { + LogUtils.warn("Invalid crop stage: " + cropStageID); + customProvider.removeAnythingAt(cropLocation); + } + } + // remove dead crops + if (deadCrops.contains(cropStageID)) { + customProvider.removeAnythingAt(cropLocation); + } + + SimpleLocation simpleLocation = SimpleLocation.of(location); + Optional optionalPot = plugin.getWorldManager().getPotAt(simpleLocation); + if (optionalPot.isEmpty()) { + LogUtils.warn("Found a pot without data broken by " + state.getPlayer().getName() + " at " + simpleLocation); + return FunctionResult.RETURN; + } + if (!optionalPot.get().getKey().equals(pot.getKey())) { + LogUtils.warn("Found a pot having inconsistent data broken by " + state.getPlayer().getName() + " at " + simpleLocation + "."); + plugin.getWorldManager().removePotAt(simpleLocation); + return FunctionResult.RETURN; + } + // fire event + PotBreakEvent breakEvent = new PotBreakEvent(blockWrapper.getPlayer(), location, optionalPot.get()); + if (EventUtils.fireAndCheckCancel(breakEvent)) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // remove data + plugin.getWorldManager().removePotAt(simpleLocation); + pot.trigger(ActionTrigger.BREAK, state); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL) + ); + + this.registerItemFunction(potItemID, FunctionTrigger.PLACE, + /* + * Place the pot + */ + new CFunction(conditionWrapper -> { + if (!(conditionWrapper instanceof PlaceBlockWrapper blockWrapper)) { + return FunctionResult.PASS; + } + Location location = blockWrapper.getPlacedBlock().getLocation(); + Player player = blockWrapper.getPlayer(); + // check place requirements + State state = new State(player, blockWrapper.getItemInHand(), location); + if (!RequirementManager.isRequirementMet(state, pot.getPlaceRequirements())) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // check limitation + SimpleLocation simpleLocation = SimpleLocation.of(location); + if (plugin.getWorldManager().isReachLimit(simpleLocation, ItemType.POT)) { + pot.trigger(ActionTrigger.REACH_LIMIT, new State(player, blockWrapper.getItemInHand(), location)); + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // fire event + PotPlaceEvent potPlaceEvent = new PotPlaceEvent(player, location, pot); + if (EventUtils.fireAndCheckCancel(potPlaceEvent)) { + return FunctionResult.CANCEL_EVENT_AND_RETURN; + } + // add data + plugin.getWorldManager().addPotAt(new MemoryPot(simpleLocation, pot.getKey()), simpleLocation); + pot.trigger(ActionTrigger.PLACE, state); + return FunctionResult.RETURN; + }, CFunction.FunctionPriority.NORMAL)); + } + } + + private void registerItemFunction(String item, FunctionTrigger trigger, CFunction... function) { + if (itemID2FunctionMap.containsKey(item)) { + var previous = itemID2FunctionMap.get(item); + TreeSet previousFunctions = previous.get(trigger); + if (previousFunctions == null) { + previous.put(trigger, new TreeSet<>(List.of(function))); + } else { + previousFunctions.addAll(List.of(function)); + } + } else { + TreeSet list = new TreeSet<>(List.of(function)); + itemID2FunctionMap.put(item, new HashMap<>(Map.of(trigger, list))); + } + } + + @SuppressWarnings("DuplicatedCode") + public void handlePlayerInteractBlock( + Player player, + Block clickedBlock, + BlockFace clickedFace, + Cancellable event + ) { + if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) + return; + + // check anti-grief + if (!antiGrief.canInteract(player, clickedBlock.getLocation())) + return; + + // check pot firstly because events might be cancelled + var condition = new InteractBlockWrapper(player, clickedBlock, clickedFace); + String blockID = customProvider.getBlockID(clickedBlock); + TreeSet blockFunctions = Optional.ofNullable(itemID2FunctionMap.get(blockID)) + .map(map -> map.get(FunctionTrigger.BE_INTERACTED)) + .orElse(null); + if (handleFunctions(blockFunctions, condition, event)) { + return; + } + // Then check item in hand + String itemID = customProvider.getItemID(condition.getItemInHand()); + Optional.ofNullable(itemID2FunctionMap.get(itemID)) + .map(map -> map.get(FunctionTrigger.INTERACT_AT)) + .ifPresent(itemFunctions -> handleFunctions(itemFunctions, condition, event)); + } + + public void handlePlayerInteractAir( + Player player, + Cancellable event + ) { + if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) + return; + + // check anti-grief + if (!antiGrief.canInteract(player, player.getLocation())) + return; + + var condition = new InteractWrapper(player, null); + // check item in hand + String itemID = customProvider.getItemID(condition.getItemInHand()); + Optional.ofNullable(itemID2FunctionMap.get(itemID)) + .map(map -> map.get(FunctionTrigger.INTERACT_AIR)) + .ifPresent(cFunctions -> handleFunctions(cFunctions, condition, event)); + } + + public void handlePlayerBreakBlock( + Player player, + Block brokenBlock, + Cancellable event + ) { + if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) + return; + + /* + No need to check anti-grief here as the event should be cancelled by the anti-grief plugin + */ + + // check blocks, no need to check item in hand + String blockID = customProvider.getBlockID(brokenBlock); + Optional.ofNullable(itemID2FunctionMap.get(blockID)) + .map(map -> map.get(FunctionTrigger.BREAK)) + .ifPresent(cFunctions -> handleFunctions(cFunctions, new BreakBlockWrapper(player, brokenBlock), event)); + } + + @SuppressWarnings("DuplicatedCode") + public void handlePlayerInteractFurniture( + Player player, + Location location, + String id, + Entity baseEntity, + Cancellable event + ) { + if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) + return; + + // check anti-grief + if (!antiGrief.canInteract(player, location)) + return; + + var condition = new InteractFurnitureWrapper(player, location, id, baseEntity); + // check furniture firstly + TreeSet functions = Optional.ofNullable(itemID2FunctionMap.get(id)).map(map -> map.get(FunctionTrigger.BE_INTERACTED)).orElse(null); + if (handleFunctions(functions, condition, event)) { + return; + } + // Then check item in hand + String itemID = customProvider.getItemID(condition.getItemInHand()); + Optional.ofNullable(itemID2FunctionMap.get(itemID)) + .map(map -> map.get(FunctionTrigger.INTERACT_AT)) + .ifPresent(cFunctions -> handleFunctions(cFunctions, condition, event)); + } + + public void handlePlayerPlaceFurniture( + Player player, + Location location, + String id, + Cancellable event + ) { + if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) + return; + + /* + No need to check anti-grief here as the event should be cancelled by the anti-grief plugin + */ + + // check furniture, no need to check item in hand + Optional.ofNullable(itemID2FunctionMap.get(id)) + .map(map -> map.get(FunctionTrigger.PLACE)) + .ifPresent(cFunctions -> handleFunctions(cFunctions, new PlaceFurnitureWrapper(player, location, id), event)); + } + + public void handlePlayerBreakFurniture( + Player player, + Location location, + String id, + Cancellable event + ) { + if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) + return; + + /* + No need to check anti-grief here as the event should be handled by ItemsAdder/Oraxen + */ + + // check furniture, no need to check item in hand + Optional.ofNullable(itemID2FunctionMap.get(id)) + .map(map -> map.get(FunctionTrigger.BREAK)) + .ifPresent(cFunctions -> handleFunctions(cFunctions, new BreakFurnitureWrapper(player, location, id), event)); + } + + public void handlePlayerPlaceBlock(Player player, Block block, String blockID, Cancellable event) { + if (!plugin.getWorldManager().isMechanicEnabled(player.getWorld())) + return; + + /* + No need to check anti-grief here as the event should be cancelled by the anti-grief plugin + */ + + // check furniture, no need to check item in hand + Optional.ofNullable(itemID2FunctionMap.get(blockID)) + .map(map -> map.get(FunctionTrigger.PLACE)) + .ifPresent(cFunctions -> handleFunctions(cFunctions, new PlaceBlockWrapper(player, block, blockID), event)); + } + + public void handleEntityBreakBlock(Entity entity, Block block, Cancellable event) { + if (entity instanceof Player player) { + handlePlayerBreakBlock(player, block, event); + } else { + Pot pot = getPotByBlock(block); + if (pot != null) { + // prevent entities from breaking pots with requirements + if (pot.getBreakRequirements().length != 0) { + event.setCancelled(true); + return; + } + + Location cropLocation = block.getLocation().clone().add(0,1,0); + String cropStageID = customProvider.getSomethingAt(cropLocation); + Crop.Stage stage = stage2CropStageMap.get(cropStageID); + if (stage != null) { + //State state = new State(null, new ItemStack(Material.AIR), cropLocation); + // if crops are above, check the break requirements for crops + Crop crop = getCropByStageID(cropStageID); + if (crop.getBreakRequirements().length != 0 || stage.getBreakRequirements().length != 0) { + event.setCancelled(true); + return; + } + Optional optionalWorldCrop = plugin.getWorldManager().getCropAt(SimpleLocation.of(cropLocation)); + if (optionalWorldCrop.isPresent()) { + if (!optionalWorldCrop.get().getKey().equals(crop.getKey())) { + LogUtils.warn("Found a crop having inconsistent data broken by " + entity.getType() + " at " + cropLocation + "."); + } + } else { + plugin.debug("Found a crop without data broken by " + entity.getType() + " at " + cropLocation + ". " + + "You can safely ignore this if the crop is spawned in the wild."); + } + // trigger actions + //stage.trigger(ActionTrigger.BREAK, state); + //crop.trigger(ActionTrigger.BREAK, state); + plugin.getWorldManager().removeCropAt(SimpleLocation.of(cropLocation)); + customProvider.removeAnythingAt(cropLocation); + } + + if (deadCrops.contains(cropStageID)) { + customProvider.removeAnythingAt(cropLocation); + } + + plugin.getWorldManager().removePotAt(SimpleLocation.of(block.getLocation())); + //pot.trigger(ActionTrigger.BREAK, new State(null, new ItemStack(Material.AIR), block.getLocation())); + } + } + } + + private boolean handleFunctions(Collection functions, ConditionWrapper wrapper, @Nullable Cancellable event) { + if (functions == null) return false; + for (CFunction function : functions) { + FunctionResult result = function.apply(wrapper); + if (result == FunctionResult.CANCEL_EVENT_AND_RETURN) { + if (event != null) event.setCancelled(true); + return true; + } + if (result == FunctionResult.RETURN) + return true; + } + return false; + } + + @Override + public void updatePotState(Location location, Pot pot, boolean hasWater, Fertilizer fertilizer) { + this.customProvider.placeBlock( + location, + pot.getBlockState( + hasWater, + Optional.ofNullable(fertilizer) + .map(Fertilizer::getFertilizerType) + .orElse(null) + ) + ); + } + + @NotNull + @Override + public Collection getPotInRange(Location baseLocation, int width, int length, float yaw, String potID) { + ArrayList potLocations = new ArrayList<>(); + int extend = (width-1) / 2; + int extra = (width-1) % 2; + switch ((int) ((yaw + 180) / 45)) { + case 0 -> { + // -180 ~ -135 + for (int i = -extend; i <= extend + extra; i++) { + for (int j = 0; j < length; j++) { + potLocations.add(baseLocation.clone().add(i, 0, -j)); + } + } + } + case 1 -> { + // -135 ~ -90 + for (int i = -extend - extra; i <= extend; i++) { + for (int j = 0; j < length; j++) { + potLocations.add(baseLocation.clone().add(j, 0, i)); + } + } + } + case 2 -> { + // -90 ~ -45 + for (int i = -extend; i <= extend + extra; i++) { + for (int j = 0; j < length; j++) { + potLocations.add(baseLocation.clone().add(j, 0, i)); + } + } + } + case 3 -> { + // -45 ~ 0 + for (int i = -extend; i <= extend + extra; i++) { + for (int j = 0; j < length; j++) { + potLocations.add(baseLocation.clone().add(i, 0, j)); + } + } + } + case 4 -> { + // 0 ~ 45 + for (int i = -extend - extra; i <= extend; i++) { + for (int j = 0; j < length; j++) { + potLocations.add(baseLocation.clone().add(i, 0, j)); + } + } + } + case 5 -> { + // 45 ~ 90 + for (int i = -extend; i <= extend + extra; i++) { + for (int j = 0; j < length; j++) { + potLocations.add(baseLocation.clone().add(-j, 0, i)); + } + } + } + case 6 -> { + // 90 ~ 135 + for (int i = -extend - extra; i <= extend; i++) { + for (int j = 0; j < length; j++) { + potLocations.add(baseLocation.clone().add(-j, 0, i)); + } + } + } + case 7 -> { + // 135 ~ 180 + for (int i = -extend - extra; i <= extend; i++) { + for (int j = 0; j < length; j++) { + potLocations.add(baseLocation.clone().add(i, 0, -j)); + } + } + } + default -> potLocations.add(baseLocation); + } + return potLocations.stream().filter(it -> { + Pot pot = getPotByBlock(it.getBlock()); + if (pot == null) return false; + return pot.getKey().equals(potID); + }).toList(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java new file mode 100644 index 0000000..a71463d --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/AbstractCustomListener.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.custom; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.WorldManager; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.*; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.ItemSpawnEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +public abstract class AbstractCustomListener implements Listener { + + protected ItemManagerImpl itemManager; + + public AbstractCustomListener(ItemManagerImpl itemManager) { + this.itemManager = itemManager; + } + + @EventHandler (ignoreCancelled = true) + public void onInteractBlock(PlayerInteractEvent event) { + if (event.getHand() != EquipmentSlot.HAND) + return; + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) + return; + + Player player = event.getPlayer(); + this.itemManager.handlePlayerInteractBlock( + player, + event.getClickedBlock(), + event.getBlockFace(), + event + ); + } + + @EventHandler (ignoreCancelled = false) + public void onInteractAir(PlayerInteractEvent event) { + if (event.getHand() != EquipmentSlot.HAND) + return; + if (event.getAction() != Action.RIGHT_CLICK_AIR) + return; + + Player player = event.getPlayer(); + this.itemManager.handlePlayerInteractAir( + player, + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onBreakBlock(BlockBreakEvent event) { + Player player = event.getPlayer(); + this.itemManager.handlePlayerBreakBlock( + player, + event.getBlock(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceBlock(BlockPlaceEvent event) { + onPlaceBlock( + event.getPlayer(), + event.getBlock(), + event.getBlockPlaced().getType().name(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onItemSpawn(ItemSpawnEvent event) { + Item item = event.getEntity(); + ItemStack itemStack = item.getItemStack(); + String itemID = this.itemManager.getItemID(itemStack); + Sprinkler sprinkler = this.itemManager.getSprinklerBy3DItemID(itemID); + if (sprinkler != null) { + ItemStack newItem = this.itemManager.getItemStack(null, sprinkler.get2DItemID()); + if (newItem != null) { + newItem.setAmount(itemStack.getAmount()); + item.setItemStack(newItem); + } + return; + } + + Pot pot = this.itemManager.getPotByBlockID(itemID); + if (pot != null) { + ItemStack newItem = this.itemManager.getItemStack(null, pot.getDryItem()); + if (newItem != null) { + newItem.setAmount(itemStack.getAmount()); + item.setItemStack(newItem); + } + return; + } + } + + @EventHandler (ignoreCancelled = true) + public void onTrampling(EntityChangeBlockEvent event) { + Block block = event.getBlock(); + if (block.getType() == Material.FARMLAND && event.getTo() == Material.DIRT) { + if (ConfigManager.preventTrampling()) { + event.setCancelled(true); + return; + } + itemManager.handleEntityBreakBlock(event.getEntity(), block, event); + } + } + + @EventHandler (ignoreCancelled = true) + public void onMoistureChange(MoistureChangeEvent event) { + if (ConfigManager.disableMoisture()) + event.setCancelled(true); + } + + @EventHandler (ignoreCancelled = true) + public void onPistonExtend(BlockPistonExtendEvent event) { + WorldManager manager = CustomCropsPlugin.get().getWorldManager(); + for (Block block : event.getBlocks()) { + if (manager.getBlockAt(SimpleLocation.of(block.getLocation())).isPresent()) { + event.setCancelled(true); + return; + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onPistonRetract(BlockPistonRetractEvent event) { + WorldManager manager = CustomCropsPlugin.get().getWorldManager(); + for (Block block : event.getBlocks()) { + if (manager.getBlockAt(SimpleLocation.of(block.getLocation())).isPresent()) { + event.setCancelled(true); + return; + } + } + } + + public void onPlaceBlock(Player player, Block block, String blockID, Cancellable event) { + this.itemManager.handlePlayerPlaceBlock(player, block, blockID, event); + } + + public void onBreakFurniture(Player player, Location location, String id, Cancellable event) { + this.itemManager.handlePlayerBreakFurniture(player, location, id, event); + } + + public void onPlaceFurniture(Player player, Location location, String id, Cancellable event) { + this.itemManager.handlePlayerPlaceFurniture(player, location, id, event); + } + + public void onInteractFurniture(Player player, Location location, String id, @Nullable Entity baseEntity, Cancellable event) { + this.itemManager.handlePlayerInteractFurniture(player, location, id, baseEntity, event); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java new file mode 100644 index 0000000..acaaa4b --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderListener.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.custom.itemsadder; + +import dev.lone.itemsadder.api.CustomFurniture; +import dev.lone.itemsadder.api.Events.CustomBlockPlaceEvent; +import dev.lone.itemsadder.api.Events.FurnitureBreakEvent; +import dev.lone.itemsadder.api.Events.FurnitureInteractEvent; +import dev.lone.itemsadder.api.Events.FurniturePlaceSuccessEvent; +import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; +import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; + +public class ItemsAdderListener extends AbstractCustomListener { + + public ItemsAdderListener(ItemManagerImpl itemManager) { + super(itemManager); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceCustomBlock(CustomBlockPlaceEvent event) { + super.onPlaceBlock( + event.getPlayer(), + event.getBlock(), + event.getNamespacedID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceFurniture(FurniturePlaceSuccessEvent event) { + Entity entity = event.getBukkitEntity(); + if (entity == null) return; + // player would be null if furniture is placed with API + if (event.getPlayer() == null) return; + super.onPlaceFurniture( + event.getPlayer(), + entity.getLocation(), + event.getNamespacedID(), + null + ); + } + + @EventHandler (ignoreCancelled = true) + public void onBreakFurniture(FurnitureBreakEvent event) { + CustomFurniture customFurniture = event.getFurniture(); + if (customFurniture == null) return; + Entity entity = customFurniture.getEntity(); + if (entity == null) return; + super.onBreakFurniture( + event.getPlayer(), + entity.getLocation(), + event.getNamespacedID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onInteractFurniture(FurnitureInteractEvent event) { + CustomFurniture customFurniture = event.getFurniture(); + if (customFurniture == null) return; + Entity entity = customFurniture.getEntity(); + if (entity == null) return; + super.onInteractFurniture(event.getPlayer(), + entity.getLocation(), + event.getNamespacedID(), + entity, + event + ); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java new file mode 100644 index 0000000..28fb77c --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/itemsadder/ItemsAdderProvider.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.custom.itemsadder; + +import dev.lone.itemsadder.api.CustomBlock; +import dev.lone.itemsadder.api.CustomFurniture; +import dev.lone.itemsadder.api.CustomStack; +import net.momirealms.customcrops.mechanic.item.CustomProvider; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.ItemStack; + +public class ItemsAdderProvider implements CustomProvider { + + @Override + public boolean removeBlock(Location location) { + Block block = location.getBlock(); + if (block.getType() == Material.AIR) + return false; + if (!CustomBlock.remove(location)) { + block.setType(Material.AIR); + } + return true; + } + + @Override + public void placeCustomBlock(Location location, String id) { + CustomBlock.place(id, location); + } + + @Override + public void placeFurniture(Location location, String id) { + CustomFurniture.spawnPreciseNonSolid(id, location); + } + + @Override + public void removeFurniture(Entity entity) { + CustomFurniture.remove(entity, false); + } + + @Override + public String getBlockID(Block block) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + if (customBlock == null) { + return block.getType().name(); + } + return customBlock.getNamespacedID(); + } + + @Override + public String getItemID(ItemStack itemInHand) { + CustomStack customStack = CustomStack.byItemStack(itemInHand); + if (customStack == null) { + return itemInHand.getType().name(); + } + return customStack.getNamespacedID(); + } + + @Override + public ItemStack getItemStack(String id) { + CustomStack customStack = CustomStack.getInstance(id); + if (customStack == null) { + return null; + } + return customStack.getItemStack(); + } + + @Override + public String getEntityID(Entity entity) { + CustomFurniture customFurniture = CustomFurniture.byAlreadySpawned(entity); + if (customFurniture == null) { + return entity.getType().name(); + } + return customFurniture.getNamespacedID(); + } + + @Override + public boolean isFurniture(Entity entity) { + return CustomFurniture.byAlreadySpawned(entity) != null; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java new file mode 100644 index 0000000..e1351d7 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenListener.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.custom.oraxen; + +import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureBreakEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurnitureInteractEvent; +import io.th0rgal.oraxen.api.events.furniture.OraxenFurniturePlaceEvent; +import io.th0rgal.oraxen.api.events.noteblock.OraxenNoteBlockPlaceEvent; +import io.th0rgal.oraxen.api.events.stringblock.OraxenStringBlockPlaceEvent; +import net.momirealms.customcrops.mechanic.item.ItemManagerImpl; +import net.momirealms.customcrops.mechanic.item.custom.AbstractCustomListener; +import org.bukkit.event.EventHandler; + +public class OraxenListener extends AbstractCustomListener { + + public OraxenListener(ItemManagerImpl itemManager) { + super(itemManager); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceCustomBlock(OraxenNoteBlockPlaceEvent event) { + super.onPlaceBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceCustomBlock(OraxenStringBlockPlaceEvent event) { + super.onPlaceBlock( + event.getPlayer(), + event.getBlock(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onPlaceFurniture(OraxenFurniturePlaceEvent event) { + super.onPlaceFurniture( + event.getPlayer(), + event.getBlock().getLocation(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onBreakFurniture(OraxenFurnitureBreakEvent event) { + super.onBreakFurniture( + event.getPlayer(), + event.getBlock().getLocation(), + event.getMechanic().getItemID(), + event + ); + } + + @EventHandler (ignoreCancelled = true) + public void onInteractFurniture(OraxenFurnitureInteractEvent event) { + super.onInteractFurniture( + event.getPlayer(), + event.getBlock().getLocation(), + event.getMechanic().getItemID(), + event.getBaseEntity(), + event + ); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java new file mode 100644 index 0000000..efb078a --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/custom/oraxen/OraxenProvider.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.custom.oraxen; + +import io.th0rgal.oraxen.api.OraxenBlocks; +import io.th0rgal.oraxen.api.OraxenFurniture; +import io.th0rgal.oraxen.api.OraxenItems; +import io.th0rgal.oraxen.items.ItemBuilder; +import io.th0rgal.oraxen.mechanics.provided.gameplay.furniture.FurnitureMechanic; +import io.th0rgal.oraxen.mechanics.provided.gameplay.noteblock.NoteBlockMechanic; +import net.momirealms.customcrops.mechanic.item.CustomProvider; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Rotation; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.ItemStack; + +public class OraxenProvider implements CustomProvider { + + @Override + public boolean removeBlock(Location location) { + Block block = location.getBlock(); + if (block.getType() == Material.AIR) { + return false; + } + if (OraxenBlocks.remove(location, null, false)) { + block.setType(Material.AIR); + } + return true; + } + + @Override + public void placeCustomBlock(Location location, String id) { + OraxenBlocks.place(id, location); + } + + @Override + public void placeFurniture(Location location, String id) { + OraxenFurniture.place(id, location, Rotation.NONE, BlockFace.UP); + } + + @Override + public void removeFurniture(Entity entity) { + OraxenFurniture.remove(entity, null); + } + + @Override + public String getBlockID(Block block) { + NoteBlockMechanic mechanic = OraxenBlocks.getNoteBlockMechanic(block); + if (mechanic == null) { + return block.getType().name(); + } + return mechanic.getItemID(); + } + + @Override + public String getItemID(ItemStack itemInHand) { + String id = OraxenItems.getIdByItem(itemInHand); + if (id == null) { + return itemInHand.getType().name(); + } + return id; + } + + @Override + public ItemStack getItemStack(String id) { + ItemBuilder builder = OraxenItems.getItemById(id); + if (builder == null) { + return null; + } + return builder.build(); + } + + @Override + public String getEntityID(Entity entity) { + FurnitureMechanic mechanic = OraxenFurniture.getFurnitureMechanic(entity); + if (mechanic == null) { + return entity.getType().name(); + } + return mechanic.getItemID(); + } + + @Override + public boolean isFurniture(Entity entity) { + return OraxenFurniture.isFurniture(entity); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/CFunction.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/CFunction.java new file mode 100644 index 0000000..23a83db --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/CFunction.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.function; + +import net.momirealms.customcrops.mechanic.item.function.wrapper.ConditionWrapper; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class CFunction implements Comparable { + + private static int functionID = 0; + private final Function function; + private final FunctionPriority priority; + private final int id; + + public CFunction(Function function, FunctionPriority priority) { + this.function = function; + this.priority = priority; + this.id = functionID++; + } + + public FunctionResult apply(ConditionWrapper wrapper) { + return function.apply(wrapper); + } + + public Function getFunction() { + return function; + } + + public FunctionPriority getPriority() { + return priority; + } + + @Override + public int compareTo(@NotNull CFunction o) { + if (this.priority.ordinal() > o.priority.ordinal()) { + return 1; + } else if (this.priority.ordinal() < o.priority.ordinal()) { + return -1; + } + return Integer.compare(this.id, o.id); + } + + public static void resetID() { + functionID = 0; + } + + public enum FunctionPriority { + + HIGHEST, + HIGH, + NORMAL, + LOW, + LOWEST; + + public boolean isHigherThan(FunctionPriority priority) { + return this.ordinal() < priority.ordinal(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionResult.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionResult.java new file mode 100644 index 0000000..a2036f5 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionResult.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.function; + +public enum FunctionResult { + + PASS, + RETURN, + CANCEL_EVENT_AND_RETURN +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionTrigger.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionTrigger.java new file mode 100644 index 0000000..540015d --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/FunctionTrigger.java @@ -0,0 +1,9 @@ +package net.momirealms.customcrops.mechanic.item.function; + +public enum FunctionTrigger { + PLACE, + BREAK, + BE_INTERACTED, + INTERACT_AT, + INTERACT_AIR +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakBlockWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakBlockWrapper.java new file mode 100644 index 0000000..0851a6e --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakBlockWrapper.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.function.wrapper; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +public class BreakBlockWrapper extends BreakWrapper { + + private final Block brokenBlock; + + public BreakBlockWrapper(Player player, Block brokenBlock) { + super(player, brokenBlock.getLocation()); + this.brokenBlock = brokenBlock; + } + + public Block getBrokenBlock() { + return brokenBlock; + } +} + diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakFurnitureWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakFurnitureWrapper.java new file mode 100644 index 0000000..c705fca --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakFurnitureWrapper.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.function.wrapper; + +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class BreakFurnitureWrapper extends BreakWrapper { + + private final Location location; + private final String id; + + public BreakFurnitureWrapper(Player player, Location location, String id) { + super(player, location); + this.location = location; + this.id = id; + } + + @NotNull + public Location getLocation() { + return location; + } + + @NotNull + public String getID() { + return id; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/CurrentState.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakWrapper.java similarity index 76% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/CurrentState.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakWrapper.java index f99382d..90c0d75 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/CurrentState.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/BreakWrapper.java @@ -15,26 +15,21 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.requirement; +package net.momirealms.customcrops.mechanic.item.function.wrapper; import org.bukkit.Location; import org.bukkit.entity.Player; -public class CurrentState { +public class BreakWrapper extends ConditionWrapper { private final Location location; - private final Player player; - public CurrentState(Location location, Player player) { + public BreakWrapper(Player player, Location location) { + super(player); this.location = location; - this.player = player; } public Location getLocation() { return location; } - - public Player getPlayer() { - return player; - } -} \ No newline at end of file +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/ConditionWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/ConditionWrapper.java new file mode 100644 index 0000000..2cada63 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/ConditionWrapper.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.function.wrapper; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public abstract class ConditionWrapper { + + private final Player player; + private final ItemStack itemInHand; + + public ConditionWrapper(Player player) { + this.player = player; + this.itemInHand = player.getInventory().getItemInMainHand(); + } + + public Player getPlayer() { + return player; + } + + public ItemStack getItemInHand() { + return itemInHand; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractBlockWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractBlockWrapper.java new file mode 100644 index 0000000..77f9a64 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractBlockWrapper.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.function.wrapper; + +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; + +public class InteractBlockWrapper extends InteractWrapper { + + private final Block clickedBlock; + private final BlockFace clickedFace; + + public InteractBlockWrapper(Player player, Block clickedBlock, BlockFace clickedFace) { + super(player, clickedBlock.getLocation()); + this.clickedBlock = clickedBlock; + this.clickedFace = clickedFace; + } + + public Block getClickedBlock() { + return clickedBlock; + } + + public BlockFace getClickedFace() { + return clickedFace; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/item/DefaultImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractFurnitureWrapper.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/integration/item/DefaultImpl.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractFurnitureWrapper.java index a0326d2..f01e487 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/item/DefaultImpl.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractFurnitureWrapper.java @@ -15,25 +15,32 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration.item; +package net.momirealms.customcrops.mechanic.item.function.wrapper; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.integration.ItemInterface; -import net.momirealms.customcrops.util.ConfigUtils; -import org.bukkit.Material; +import org.bukkit.Location; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class DefaultImpl implements ItemInterface { +public class InteractFurnitureWrapper extends InteractWrapper { + + private final String id; + private final Entity entity; + + public InteractFurnitureWrapper(Player player, Location location, String id, @Nullable Entity baseEntity) { + super(player, location); + this.entity = baseEntity; + this.id = id; + } @Nullable - @Override - public ItemStack build(String id, Player player) { - if (ConfigUtils.isVanillaItem(id)) { - return new ItemStack(Material.valueOf(id)); - } else { - return CustomCrops.getInstance().getPlatformInterface().getItemStack(id); - } + public Entity getEntity() { + return entity; + } + + @NotNull + public String getID() { + return id; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/ExpressionOr.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractWrapper.java similarity index 65% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/ExpressionOr.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractWrapper.java index 5b491d0..f14201f 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/ExpressionOr.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/InteractWrapper.java @@ -15,19 +15,21 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.requirement.papi; +package net.momirealms.customcrops.mechanic.item.function.wrapper; +import org.bukkit.Location; import org.bukkit.entity.Player; -import java.util.List; +public class InteractWrapper extends ConditionWrapper { -public record ExpressionOr(List requirements) implements PapiRequirement{ + private final Location location; - @Override - public boolean isMet(Player player) { - for (PapiRequirement requirement : requirements) { - if (requirement.isMet(player)) return true; - } - return false; + public InteractWrapper(Player player, Location location) { + super(player); + this.location = location; } -} \ No newline at end of file + + public Location getLocation() { + return location; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiEquals.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceBlockWrapper.java similarity index 62% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiEquals.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceBlockWrapper.java index 2fae860..ab66813 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiEquals.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceBlockWrapper.java @@ -15,18 +15,22 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.requirement.papi; +package net.momirealms.customcrops.mechanic.item.function.wrapper; -import me.clip.placeholderapi.PlaceholderAPI; +import org.bukkit.block.Block; import org.bukkit.entity.Player; -import java.util.Objects; +public class PlaceBlockWrapper extends PlaceWrapper { -public record PapiEquals(String papi, String requirement) implements PapiRequirement{ + private final Block placedBlock; - @Override - public boolean isMet(Player player) { - String value = PlaceholderAPI.setPlaceholders(player, papi); - return Objects.equals(value, PlaceholderAPI.setPlaceholders(player, requirement)); + public PlaceBlockWrapper(Player player, Block placedBlock, String blockID) { + super(player, placedBlock.getLocation(), blockID); + this.placedBlock = placedBlock; + } + + public Block getPlacedBlock() { + return placedBlock; } } + diff --git a/plugin/src/main/java/net/momirealms/customcrops/integration/JobInterface.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceFurnitureWrapper.java similarity index 72% rename from plugin/src/main/java/net/momirealms/customcrops/integration/JobInterface.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceFurnitureWrapper.java index a697cd0..31c5c11 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/integration/JobInterface.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceFurnitureWrapper.java @@ -15,12 +15,14 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.integration; +package net.momirealms.customcrops.mechanic.item.function.wrapper; +import org.bukkit.Location; import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; -public interface JobInterface { - void addXp(Player player, double amount, @Nullable String job); - int getLevel(Player player, @Nullable String job); +public class PlaceFurnitureWrapper extends PlaceWrapper { + + public PlaceFurnitureWrapper(Player player, Location location, String id) { + super(player, location, id); + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceWrapper.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceWrapper.java new file mode 100644 index 0000000..668f31d --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/function/wrapper/PlaceWrapper.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.function.wrapper; + +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class PlaceWrapper extends ConditionWrapper { + + private final Location location; + private final String id; + + public PlaceWrapper(Player player, Location location, String id) { + super(player); + this.location = location; + this.id = id; + } + + public Location getLocation() { + return location; + } + + public String getId() { + return id; + } +} + diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/AbstractFertilizer.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/AbstractFertilizer.java new file mode 100644 index 0000000..c7e6402 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/AbstractFertilizer.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.impl; + +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.item.FertilizerType; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.AbstractEventItem; + +import java.util.HashMap; +import java.util.HashSet; + +public class AbstractFertilizer extends AbstractEventItem implements Fertilizer { + + private final String key; + private final String itemID; + private final int times; + private final FertilizerType fertilizerType; + private final HashSet potWhitelist; + private final boolean beforePlant; + private final String icon; + private final Requirement[] requirements; + + public AbstractFertilizer( + String key, + String itemID, + int times, + FertilizerType fertilizerType, + HashSet potWhitelist, + boolean beforePlant, + String icon, + Requirement[] requirements, + HashMap actionMap + ) { + super(actionMap); + this.key = key; + this.itemID = itemID; + this.times = times; + this.fertilizerType = fertilizerType; + this.potWhitelist = potWhitelist; + this.beforePlant = beforePlant; + this.icon = icon; + this.requirements = requirements; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getItemID() { + return itemID; + } + + @Override + public int getTimes() { + return times; + } + + @Override + public FertilizerType getFertilizerType() { + return fertilizerType; + } + + @Override + public HashSet getPotWhitelist() { + return potWhitelist; + } + + @Override + public boolean isBeforePlant() { + return beforePlant; + } + + @Override + public String getIcon() { + return icon; + } + + @Override + public Requirement[] getRequirements() { + return requirements; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java new file mode 100644 index 0000000..e2f1b3a --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/CropConfig.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.impl; + +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.condition.Conditions; +import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; +import net.momirealms.customcrops.api.mechanic.item.BoneMeal; +import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.AbstractEventItem; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; + +public class CropConfig extends AbstractEventItem implements Crop { + + private final String key; + private final String seedID; + private final int maxPoints; + private final boolean rotation; + private final ItemCarrier carrier; + private final Requirement[] plantRequirements; + private final Requirement[] breakRequirements; + private final Requirement[] interactRequirements; + private final BoneMeal[] boneMeals; + private final Conditions growConditions; + private final HashSet whitelistPots; + private final DeathConditions[] deathConditions; + private final HashMap point2StageConfigMap; + private final HashMap item2StageConfigMap; + + public CropConfig( + String key, + String seedID, + ItemCarrier carrier, + HashSet whitelistPots, + boolean rotation, + int maxPoints, + BoneMeal[] boneMeals, + Conditions growConditions, + DeathConditions[] deathConditions, + HashMap actionMap, + HashMap point2StageConfigMap, + Requirement[] plantRequirements, + Requirement[] breakRequirements, + Requirement[] interactRequirements + ) { + super(actionMap); + this.key = key; + this.seedID = seedID; + this.boneMeals = boneMeals; + this.rotation = rotation; + this.maxPoints = maxPoints; + this.growConditions = growConditions; + this.deathConditions = deathConditions; + this.plantRequirements = plantRequirements; + this.breakRequirements = breakRequirements; + this.interactRequirements = interactRequirements; + this.point2StageConfigMap = point2StageConfigMap; + this.whitelistPots = whitelistPots; + this.carrier = carrier; + this.item2StageConfigMap = new HashMap<>(); + for (CropStageConfig cropStageConfig : point2StageConfigMap.values()) { + if (cropStageConfig.getStageID() != null) { + this.item2StageConfigMap.put(cropStageConfig.getStageID(), cropStageConfig); + } + } + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getSeedItemID() { + return seedID; + } + + @Override + public int getMaxPoints() { + return maxPoints; + } + + @Override + public Requirement[] getPlantRequirements() { + return plantRequirements; + } + + @Override + public Requirement[] getBreakRequirements() { + return breakRequirements; + } + + @Override + public Requirement[] getInteractRequirements() { + return interactRequirements; + } + + @Override + public Conditions getGrowConditions() { + return growConditions; + } + + @Override + public DeathConditions[] getDeathConditions() { + return deathConditions; + } + + @Override + public BoneMeal[] getBoneMeals() { + return boneMeals; + } + + @Override + public boolean isRotation() { + return rotation; + } + + @Override + public Stage getStageByPoint(int point) { + return point2StageConfigMap.get(point); + } + + @Override + public String getStageItemByPoint(int point) { + if (point >= 0) { + Stage stage = point2StageConfigMap.get(point); + if (stage != null) { + String id = stage.getStageID(); + if (id == null) return getStageItemByPoint(point-1); + return id; + } else { + return getStageItemByPoint(point-1); + } + } + return null; + } + + @Override + public Stage getStageByItemID(String itemID) { + return item2StageConfigMap.get(itemID); + } + + @Override + public Collection getStages() { + return new ArrayList<>(point2StageConfigMap.values()); + } + + @Override + public HashSet getPotWhitelist() { + return whitelistPots; + } + + @Override + public ItemCarrier getItemCarrier() { + return carrier; + } + + public static class CropStageConfig extends AbstractEventItem implements Crop.Stage { + + @Nullable + private final String stageID; + + private final int point; + private final double hologramOffset; + private final Requirement[] interactRequirements; + private final Requirement[] breakRequirements; + + public CropStageConfig( + @Nullable String stageID, + int point, + double hologramOffset, + HashMap actionMap, + Requirement[] interactRequirements, + Requirement[] breakRequirements + ) { + super(actionMap); + this.stageID = stageID; + this.point = point; + this.hologramOffset = hologramOffset; + this.interactRequirements = interactRequirements; + this.breakRequirements = breakRequirements; + } + + @Override + public double getHologramOffset() { + return hologramOffset; + } + + @Nullable + @Override + public String getStageID() { + return stageID; + } + + @Override + public int getPoint() { + return point; + } + + @Override + public Requirement[] getInteractRequirements() { + return interactRequirements; + } + + @Override + public Requirement[] getBreakRequirements() { + return breakRequirements; + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java new file mode 100644 index 0000000..5e5fc99 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/PotConfig.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.impl; + +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.FertilizerType; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; +import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.AbstractEventItem; + +import java.util.HashMap; +import java.util.HashSet; + +public class PotConfig extends AbstractEventItem implements Pot { + + private final String key; + private final int storage; + private final String dryModel; + private final String wetModel; + private final boolean enableFertilizedAppearance; + private final HashMap> fertilizedPotMap; + private final PassiveFillMethod[] passiveFillMethods; + private final WaterBar waterBar; + private final Requirement[] placeRequirements; + private final Requirement[] breakRequirements; + private final Requirement[] useRequirements; + private final boolean acceptRainDrop; + private final boolean acceptNearbyWater; + + public PotConfig( + String key, + int storage, + boolean acceptRainDrop, + boolean acceptNearbyWater, + String dryModel, + String wetModel, + boolean enableFertilizedAppearance, + HashMap> fertilizedPotMap, + WaterBar waterBar, + PassiveFillMethod[] passiveFillMethods, + HashMap actionMap, + Requirement[] placeRequirements, + Requirement[] breakRequirements, + Requirement[] useRequirements + ) { + super(actionMap); + this.key = key; + this.storage = storage; + this.acceptRainDrop = acceptRainDrop; + this.acceptNearbyWater = acceptNearbyWater; + this.enableFertilizedAppearance = enableFertilizedAppearance; + this.fertilizedPotMap = fertilizedPotMap; + this.passiveFillMethods = passiveFillMethods; + this.dryModel = dryModel; + this.wetModel = wetModel; + this.waterBar = waterBar; + this.placeRequirements = placeRequirements; + this.breakRequirements = breakRequirements; + this.useRequirements = useRequirements; + } + + @Override + public int getStorage() { + return storage; + } + + @Override + public String getKey() { + return key; + } + + @Override + public HashSet getPotBlocks() { + HashSet set = new HashSet<>(); + set.add(wetModel); + set.add(dryModel); + for (Pair pair : fertilizedPotMap.values()) { + set.add(pair.left()); + set.add(pair.right()); + } + return set; + } + + @Override + public PassiveFillMethod[] getPassiveFillMethods() { + return passiveFillMethods; + } + + @Override + public String getDryItem() { + return dryModel; + } + + @Override + public String getWetItem() { + return wetModel; + } + + @Override + public Requirement[] getPlaceRequirements() { + return placeRequirements; + } + + @Override + public Requirement[] getBreakRequirements() { + return breakRequirements; + } + + @Override + public Requirement[] getUseRequirements() { + return useRequirements; + } + + @Override + public WaterBar getWaterBar() { + return waterBar; + } + + @Override + public boolean isRainDropAccepted() { + return acceptRainDrop; + } + + @Override + public boolean isNearbyWaterAccepted() { + return acceptNearbyWater; + } + + @Override + public String getBlockState(boolean water, FertilizerType type) { + if (type != null && enableFertilizedAppearance) { + return water ? fertilizedPotMap.get(type).right() : fertilizedPotMap.get(type).left(); + } else { + return water ? wetModel : dryModel; + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/SprinklerConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/SprinklerConfig.java new file mode 100644 index 0000000..0470f3b --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/SprinklerConfig.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.impl; + +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; +import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.AbstractEventItem; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.HashSet; + +public class SprinklerConfig extends AbstractEventItem implements Sprinkler { + + private final String key; + private final int range; + private final int storage; + private final int water; + private final boolean infinite; + private final String twoDItem; + private final String threeDItem; + private final String threeDItemWithWater; + private final WaterBar waterBar; + private final HashSet potWhitelist; + private final ItemCarrier itemCarrier; + private final PassiveFillMethod[] passiveFillMethods; + private final Requirement[] placeRequirements; + private final Requirement[] breakRequirements; + private final Requirement[] useRequirements; + + public SprinklerConfig( + String key, + ItemCarrier itemCarrier, + String twoDItem, + String threeDItem, + String threeDItemWithWater, + int range, + int storage, + int water, + boolean infinite, + WaterBar waterBar, + HashSet potWhitelist, + PassiveFillMethod[] passiveFillMethods, + HashMap actionMap, + Requirement[] placeRequirements, + Requirement[] breakRequirements, + Requirement[] useRequirements + ) { + super(actionMap); + this.key = key; + this.itemCarrier = itemCarrier; + this.twoDItem = twoDItem; + this.threeDItem = threeDItem; + this.threeDItemWithWater = threeDItemWithWater; + this.range = range; + this.storage = storage; + this.infinite = infinite; + this.water = water; + this.waterBar = waterBar; + this.potWhitelist = potWhitelist; + this.passiveFillMethods = passiveFillMethods; + this.placeRequirements = placeRequirements; + this.breakRequirements = breakRequirements; + this.useRequirements = useRequirements; + } + + @Nullable + @Override + public String get2DItemID() { + return twoDItem; + } + + @NotNull + @Override + public String get3DItemID() { + return threeDItem; + } + + @Override + @Nullable + public String get3DItemWithWater() { + return threeDItemWithWater; + } + + @Override + public int getStorage() { + return storage; + } + + @Override + public int getRange() { + return range; + } + + @Override + public String getKey() { + return key; + } + + @Override + public boolean isInfinite() { + return infinite; + } + + @Override + public int getWater() { + return water; + } + + @Override + public HashSet getPotWhitelist() { + return potWhitelist; + } + + @Override + public ItemCarrier getItemCarrier() { + return itemCarrier; + } + + @Override + public PassiveFillMethod[] getPassiveFillMethods() { + return passiveFillMethods; + } + + @Override + public Requirement[] getPlaceRequirements() { + return placeRequirements; + } + + @Override + public Requirement[] getBreakRequirements() { + return breakRequirements; + } + + @Override + public Requirement[] getUseRequirements() { + return useRequirements; + } + + @Override + public WaterBar getWaterBar() { + return waterBar; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/VariationCrop.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/VariationCrop.java similarity index 76% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/crop/VariationCrop.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/VariationCrop.java index 039d99a..dc5d1b9 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/crop/VariationCrop.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/VariationCrop.java @@ -15,27 +15,27 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.crop; +package net.momirealms.customcrops.mechanic.item.impl; -import net.momirealms.customcrops.api.object.ItemMode; +import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; public class VariationCrop { private final String id; - private final ItemMode itemMode; + private final ItemCarrier itemMode; private final double chance; - public VariationCrop(String id, ItemMode itemMode, double chance) { + public VariationCrop(String id, ItemCarrier itemMode, double chance) { this.id = id; this.itemMode = itemMode; this.chance = chance; } - public String getId() { + public String getItemID() { return id; } - public ItemMode getCropMode() { + public ItemCarrier getItemCarrier() { return itemMode; } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java new file mode 100644 index 0000000..5b95b3b --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/WateringCanConfig.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.impl; + +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ScoreComponent; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.momirealms.customcrops.api.manager.AdventureManager; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.PlaceholderManager; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.WateringCan; +import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; +import net.momirealms.customcrops.api.mechanic.misc.image.WaterBar; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.AbstractEventItem; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +public class WateringCanConfig extends AbstractEventItem implements WateringCan { + + private final String key; + private final String itemID; + private final boolean infinite; + private final int width; + private final int length; + private final int storage; + private final int water; + private final HashSet potWhitelist; + private final HashSet sprinklerWhitelist; + private final boolean hasDynamicLore; + private final List lore; + private final PositiveFillMethod[] positiveFillMethods; + private final HashMap appearanceMap; + private final Requirement[] requirements; + private final WaterBar waterBar; + + public WateringCanConfig( + String key, + String itemID, + boolean infinite, + int width, + int length, + int storage, + int water, + boolean hasDynamicLore, + List lore, + HashSet potWhitelist, + HashSet sprinklerWhitelist, + PositiveFillMethod[] positiveFillMethods, + HashMap appearanceMap, + Requirement[] requirements, + HashMap actionMap, + WaterBar waterBar + ) { + super(actionMap); + this.itemID = itemID; + this.width = width; + this.length = length; + this.storage = storage; + this.hasDynamicLore = hasDynamicLore; + this.lore = lore; + this.potWhitelist = potWhitelist; + this.sprinklerWhitelist = sprinklerWhitelist; + this.positiveFillMethods = positiveFillMethods; + this.appearanceMap = appearanceMap; + this.requirements = requirements; + this.waterBar = waterBar; + this.key = key; + this.infinite = infinite; + this.water = water; + } + + @Override + public String getItemID() { + return itemID; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getLength() { + return length; + } + + @Override + public int getStorage() { + return storage; + } + + @Override + public int getWater() { + return water; + } + + @Override + public boolean hasDynamicLore() { + return hasDynamicLore; + } + + @NotNull + public PositiveFillMethod[] getPositiveFillMethods() { + return positiveFillMethods; + } + + @Override + public void updateItem(Player player, ItemStack itemStack, int water, Map args) { + NBTItem nbtItem = new NBTItem(itemStack); + if (isInfinite()) water = storage; + if (hasDynamicLore()) { + NBTCompound displayCompound = nbtItem.getOrCreateCompound("display"); + List lore = displayCompound.getStringList("Lore"); + if (ConfigManager.protectLore()) { + lore.removeIf(line -> { + Component component = GsonComponentSerializer.gson().deserialize(line); + return component instanceof ScoreComponent scoreComponent + && scoreComponent.objective().equals("water") + && scoreComponent.name().equals("cc"); + }); + } else { + lore.clear(); + } + for (String newLore : getLore()) { + ScoreComponent.Builder builder = Component.score().name("cc").objective("water"); + builder.append(AdventureManager.getInstance().getComponentFromMiniMessage( + PlaceholderManager.getInstance().parse(player, newLore, args) + )); + lore.add(GsonComponentSerializer.gson().serialize(builder.build())); + } + } + + nbtItem.setInteger("WaterAmount", water); + itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); + } + + @Override + public int getCurrentWater(ItemStack itemStack) { + if (itemStack == null || itemStack.getType() == Material.AIR) + return 0; + NBTItem nbtItem = new NBTItem(itemStack); + return nbtItem.getInteger("WaterAmount"); + } + + @Override + public HashSet getPotWhitelist() { + return potWhitelist; + } + + @Override + public HashSet getSprinklerWhitelist() { + return sprinklerWhitelist; + } + + @Override + public String getKey() { + return key; + } + + @Override + public List getLore() { + return lore; + } + + @Override + @Nullable + public WaterBar getWaterBar() { + return waterBar; + } + + @Override + public Requirement[] getRequirements() { + return requirements; + } + + @Override + public boolean isInfinite() { + return infinite; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/QualityCropConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/QualityCropConfig.java new file mode 100644 index 0000000..93b18f3 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/QualityCropConfig.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.impl.fertilizer; + +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.FertilizerType; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.QualityCrop; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; + +import java.util.HashMap; +import java.util.HashSet; + +public class QualityCropConfig extends AbstractFertilizer implements QualityCrop { + + private final double[] ratio; + private final double chance; + + public QualityCropConfig( + String key, + String itemID, + int times, + double chance, + FertilizerType fertilizerType, + HashSet potWhitelist, + boolean beforePlant, + String icon, + Requirement[] requirements, + double[] ratio, + HashMap events + ) { + super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); + this.ratio = ratio; + this.chance = chance; + } + + @Override + public double getChance() { + return chance; + } + + @Override + public double[] getRatio() { + return ratio; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SoilRetainConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SoilRetainConfig.java new file mode 100644 index 0000000..0f1f482 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SoilRetainConfig.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.impl.fertilizer; + +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.FertilizerType; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.SoilRetain; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; + +import java.util.HashMap; +import java.util.HashSet; + +public class SoilRetainConfig extends AbstractFertilizer implements SoilRetain { + + private final double chance; + + public SoilRetainConfig( + String key, + String itemID, + int times, + double chance, + FertilizerType fertilizerType, + HashSet potWhitelist, + boolean beforePlant, + String icon, + Requirement[] requirements, + HashMap events) { + super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); + this.chance = chance; + } + + @Override + public double getChance() { + return chance; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/SpeedGrow.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SpeedGrowConfig.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/SpeedGrow.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SpeedGrowConfig.java index ecfae82..b211a80 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/SpeedGrow.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/SpeedGrowConfig.java @@ -15,36 +15,40 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.fertilizer; +package net.momirealms.customcrops.mechanic.item.impl.fertilizer; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.Pair; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.bukkit.Particle; -import org.jetbrains.annotations.Nullable; +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.FertilizerType; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.SpeedGrow; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -public class SpeedGrow extends FertilizerConfig { +public class SpeedGrowConfig extends AbstractFertilizer implements SpeedGrow { private final List> pairs; - public SpeedGrow( + public SpeedGrowConfig( String key, - FertilizerType fertilizerType, + String itemID, int times, - List> pairs, - @Nullable String[] pot_whitelist, + FertilizerType fertilizerType, + HashSet potWhitelist, boolean beforePlant, - @Nullable Particle particle, - @Nullable Sound sound, String icon, - Requirement[] requirements - ) { - super(key, fertilizerType, times, 1, pot_whitelist, beforePlant, particle, sound, icon, requirements); + Requirement[] requirements, + List> pairs, + HashMap events) { + super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); this.pairs = pairs; } + @Override public int getPointBonus() { for (Pair pair : pairs) { if (Math.random() < pair.left()) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/VariationConfig.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/VariationConfig.java new file mode 100644 index 0000000..1323d23 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/VariationConfig.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.item.impl.fertilizer; + +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.FertilizerType; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.Variation; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; + +import java.util.HashMap; +import java.util.HashSet; + +public class VariationConfig extends AbstractFertilizer implements Variation { + + private final double chanceBonus; + + public VariationConfig( + String key, + String itemID, + int times, + double chance, + FertilizerType fertilizerType, + HashSet potWhitelist, + boolean beforePlant, + String icon, + Requirement[] requirements, + HashMap events) { + super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); + this.chanceBonus = chance; + } + + @Override + public double getChanceBonus() { + return chanceBonus; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/YieldIncrease.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/YieldIncreaseConfig.java similarity index 55% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/YieldIncrease.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/YieldIncreaseConfig.java index 5885bce..f47af9e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/fertilizer/YieldIncrease.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/item/impl/fertilizer/YieldIncreaseConfig.java @@ -15,37 +15,40 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.fertilizer; +package net.momirealms.customcrops.mechanic.item.impl.fertilizer; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.api.object.Pair; -import net.momirealms.customcrops.api.object.requirement.Requirement; -import org.bukkit.Particle; -import org.jetbrains.annotations.Nullable; +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.FertilizerType; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.YieldIncrease; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.mechanic.item.impl.AbstractFertilizer; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -public class YieldIncrease extends FertilizerConfig { +public class YieldIncreaseConfig extends AbstractFertilizer implements YieldIncrease { private final List> pairs; - public YieldIncrease( + public YieldIncreaseConfig( String key, - FertilizerType fertilizerType, + String itemID, int times, - double chance, - List> pairs, - @Nullable String[] pot_whitelist, + FertilizerType fertilizerType, + HashSet potWhitelist, boolean beforePlant, - @Nullable Particle particle, - @Nullable Sound sound, String icon, - Requirement[] requirements - ) { - super(key, fertilizerType, times, chance, pot_whitelist, beforePlant, particle, sound, icon, requirements); + Requirement[] requirements, + List> pairs, + HashMap events) { + super(key, itemID, times, fertilizerType, potWhitelist, beforePlant, icon, requirements, events); this.pairs = pairs; } + @Override public int getAmountBonus() { for (Pair pair : pairs) { if (Math.random() < pair.left()) { diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java new file mode 100644 index 0000000..ba2333c --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/CrowAttackAnimation.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.misc; + +import com.comphenix.protocol.events.PacketContainer; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.scheduler.CancellableTask; +import net.momirealms.customcrops.manager.PacketManager; +import net.momirealms.customcrops.utils.FakeEntityUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +public class CrowAttackAnimation { + + private CancellableTask task; + private final Location fromLocation; + private final Vector vectorDown; + private final Vector vectorUp; + private final Player[] viewers; + private int timer; + private final ItemStack flyModel; + private final ItemStack standModel; + private final int entityID = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); + private final float yaw = ThreadLocalRandom.current().nextInt(361) - 180; + + public CrowAttackAnimation(SimpleLocation cropSimpleLocation, String flyModel, String standModel) { + ArrayList viewers = new ArrayList<>(); + for (Player player : Bukkit.getOnlinePlayers()) { + if (cropSimpleLocation.isNear(SimpleLocation.of(player.getLocation()), 48)) { + viewers.add(player); + } + } + this.viewers = viewers.toArray(new Player[0]); + Location cropLocation = cropSimpleLocation.getBukkitLocation().add(0.25 + Math.random() * 0.5, 0, 0.25 + Math.random() * 0.5); + this.flyModel = CustomCropsPlugin.get().getItemManager().getItemStack(null, flyModel); + this.standModel = CustomCropsPlugin.get().getItemManager().getItemStack(null, standModel); + this.fromLocation = cropLocation.clone().add((10 * Math.sin((Math.PI * yaw)/180)), 10, (- 10 * Math.cos((Math.PI * yaw)/180))); + Location relative = cropLocation.clone().subtract(fromLocation); + this.vectorDown = new Vector(relative.getX() / 100, -0.1, relative.getZ() / 100); + this.vectorUp = new Vector(relative.getX() / 100, 0.1, relative.getZ() / 100); + } + + public void start() { + if (this.viewers.length == 0) return; + sendPacketToViewers( + FakeEntityUtils.getSpawnPacket(entityID, fromLocation, EntityType.ARMOR_STAND), + FakeEntityUtils.getVanishArmorStandMetaPacket(entityID), + FakeEntityUtils.getEquipPacket(entityID, flyModel) + ); + this.task = CustomCropsPlugin.get().getScheduler().runTaskAsyncTimer(() -> { + timer++; + if (timer < 100) { + sendPacketToViewers(FakeEntityUtils.getTeleportPacket(entityID, fromLocation.add(vectorDown), yaw)); + } else if (timer == 100){ + sendPacketToViewers(FakeEntityUtils.getEquipPacket(entityID, standModel)); + } else if (timer == 150) { + sendPacketToViewers(FakeEntityUtils.getEquipPacket(entityID, flyModel)); + } else if (timer > 150) { + sendPacketToViewers(FakeEntityUtils.getTeleportPacket(entityID, fromLocation.add(vectorUp), yaw)); + } + if (timer > 300) { + sendPacketToViewers(FakeEntityUtils.getDestroyPacket(entityID)); + task.cancel(); + } + }, 50, 50, TimeUnit.MILLISECONDS); + } + + private void sendPacketToViewers(PacketContainer... packet) { + for (Player viewer : viewers) { + PacketManager.getInstance().send(viewer, packet); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java new file mode 100644 index 0000000..871e13e --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/TempFakeItem.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.misc; + +import com.comphenix.protocol.events.PacketContainer; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.manager.PacketManager; +import net.momirealms.customcrops.utils.FakeEntityUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +public class TempFakeItem { + + private final Player[] viewers; + private final int duration; + private final int entityID = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); + private final Location location; + private final String item; + + public TempFakeItem(Location location, String item, int duration, Player viewer) { + SimpleLocation simpleLocation = SimpleLocation.of(location); + ArrayList viewers = new ArrayList<>(); + if (viewer == null) + for (Player player : Bukkit.getOnlinePlayers()) { + if (simpleLocation.isNear(SimpleLocation.of(player.getLocation()), 48)) { + viewers.add(player); + } + } + else { + viewers.add(viewer); + } + this.viewers = viewers.toArray(new Player[0]); + this.location = location; + this.item = item; + this.duration = duration; + } + + public void start() { + if (this.viewers.length == 0) return; + sendPacketToViewers( + FakeEntityUtils.getSpawnPacket(entityID, location, EntityType.ARMOR_STAND), + FakeEntityUtils.getVanishArmorStandMetaPacket(entityID), + FakeEntityUtils.getEquipPacket(entityID, CustomCropsPlugin.get().getItemManager().getItemStack(null, item)) + ); + CustomCropsPlugin.get().getScheduler().runTaskAsyncLater(() -> { + sendPacketToViewers(FakeEntityUtils.getDestroyPacket(entityID)); + }, duration * 50L, TimeUnit.MILLISECONDS); + } + + private void sendPacketToViewers(PacketContainer... packet) { + for (Player viewer : viewers) { + PacketManager.getInstance().send(viewer, packet); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNoLarger.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java similarity index 59% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNoLarger.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java index 004016a..ec28e0e 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/requirement/papi/PapiNoLarger.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/ExpressionValue.java @@ -15,16 +15,24 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.requirement.papi; +package net.momirealms.customcrops.mechanic.misc.value; -import me.clip.placeholderapi.PlaceholderAPI; +import net.momirealms.customcrops.api.mechanic.misc.Value; +import net.momirealms.customcrops.utils.ConfigUtils; import org.bukkit.entity.Player; -public record PapiNoLarger(String papi, String requirement) implements PapiRequirement{ +import java.util.HashMap; + +public class ExpressionValue implements Value { + + private final String expression; + + public ExpressionValue(String expression) { + this.expression = expression; + } @Override - public boolean isMet(Player player) { - double value = Double.parseDouble(PlaceholderAPI.setPlaceholders(player, papi)); - return value <= Double.parseDouble(PlaceholderAPI.setPlaceholders(player, requirement)); + public double get(Player player) { + return ConfigUtils.getExpressionValue(player, expression, new HashMap<>(0)); } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/Action.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/PlainValue.java similarity index 67% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/action/Action.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/PlainValue.java index 9c07cf3..5a56f70 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/action/Action.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/misc/value/PlainValue.java @@ -15,15 +15,21 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.action; +package net.momirealms.customcrops.mechanic.misc.value; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.misc.Value; import org.bukkit.entity.Player; -import org.jetbrains.annotations.Nullable; -public interface Action { +public class PlainValue implements Value { - void doOn(@Nullable Player player, @Nullable SimpleLocation cropLoc, ItemMode itemMode); + private final double value; + public PlainValue(double value) { + this.value = value; + } + + @Override + public double get(Player player) { + return value; + } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Random.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/EmptyRequirement.java similarity index 64% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Random.java rename to plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/EmptyRequirement.java index 81d05aa..6d66041 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/condition/Random.java +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/EmptyRequirement.java @@ -15,20 +15,17 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.condition; +package net.momirealms.customcrops.mechanic.requirement; -import net.momirealms.customcrops.api.object.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.requirement.State; -public class Random implements Condition { +public class EmptyRequirement implements Requirement { - private final double chance; - - public Random(double chance) { - this.chance = chance; - } + public static EmptyRequirement instance = new EmptyRequirement(); @Override - public boolean isMet(SimpleLocation simpleLocation) { - return Math.random() < chance; + public boolean isStateMet(State state) { + return true; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java new file mode 100644 index 0000000..69dbff5 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/requirement/RequirementManagerImpl.java @@ -0,0 +1,976 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.requirement; + +import net.momirealms.biomeapi.BiomeAPI; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.integration.LevelInterface; +import net.momirealms.customcrops.api.integration.SeasonInterface; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.RequirementManager; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.requirement.RequirementExpansion; +import net.momirealms.customcrops.api.mechanic.requirement.RequirementFactory; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.compatibility.VaultHook; +import net.momirealms.customcrops.compatibility.papi.ParseUtils; +import net.momirealms.customcrops.utils.ClassUtils; +import net.momirealms.customcrops.utils.ConfigUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemorySection; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +public class RequirementManagerImpl implements RequirementManager { + + private final CustomCropsPlugin plugin; + private final HashMap requirementBuilderMap; + private final String EXPANSION_FOLDER = "expansions/requirement"; + + public RequirementManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.requirementBuilderMap = new HashMap<>(); + this.registerInbuiltRequirements(); + } + + @Override + public void load() { + this.loadExpansions(); + } + + @Override + public void unload() { + } + + @Override + public void disable() { + this.requirementBuilderMap.clear(); + } + + @Override + public boolean registerRequirement(String type, RequirementFactory requirementFactory) { + if (this.requirementBuilderMap.containsKey(type)) return false; + this.requirementBuilderMap.put(type, requirementFactory); + return true; + } + + @Override + public boolean unregisterRequirement(String type) { + return this.requirementBuilderMap.remove(type) != null; + } + + private void registerInbuiltRequirements() { + this.registerTimeRequirement(); + this.registerYRequirement(); + this.registerContainRequirement(); + this.registerStartWithRequirement(); + this.registerEndWithRequirement(); + this.registerEqualsRequirement(); + this.registerBiomeRequirement(); + this.registerDateRequirement(); + this.registerPluginLevelRequirement(); + this.registerPermissionRequirement(); + this.registerWorldRequirement(); + this.registerWeatherRequirement(); + this.registerSeasonRequirement(); + this.registerGreaterThanRequirement(); + this.registerAndRequirement(); + this.registerOrRequirement(); + this.registerLevelRequirement(); + this.registerRandomRequirement(); + this.registerCoolDownRequirement(); + this.registerLessThanRequirement(); + this.registerNumberEqualRequirement(); + this.registerRegexRequirement(); + this.registerMoneyRequirement(); + this.registerEnvironmentRequirement(); + this.registerPotionEffectRequirement(); + this.registerInListRequirement(); + this.registerItemInHandRequirement(); + this.registerSneakRequirement(); + this.registerTemperatureRequirement(); + this.registerFertilizerRequirement(); + } + + @NotNull + @Override + public Requirement[] getRequirements(ConfigurationSection section, boolean advanced) { + List requirements = new ArrayList<>(); + if (section == null) { + return requirements.toArray(new Requirement[0]); + } + for (Map.Entry entry : section.getValues(false).entrySet()) { + String typeOrName = entry.getKey(); + if (hasRequirement(typeOrName)) { + requirements.add(getRequirement(typeOrName, entry.getValue())); + } else { + requirements.add(getRequirement(section.getConfigurationSection(typeOrName), advanced)); + } + } + return requirements.toArray(new Requirement[0]); + } + + @Override + public boolean hasRequirement(String type) { + return requirementBuilderMap.containsKey(type); + } + + @NotNull + @Override + public Requirement getRequirement(ConfigurationSection section, boolean advanced) { + if (section == null) return EmptyRequirement.instance; + List actionList = null; + if (advanced) { + actionList = new ArrayList<>(); + if (section.contains("not-met-actions")) { + for (Map.Entry entry : Objects.requireNonNull(section.getConfigurationSection("not-met-actions")).getValues(false).entrySet()) { + if (entry.getValue() instanceof MemorySection inner) { + actionList.add(plugin.getActionManager().getAction(inner)); + } + } + } + if (actionList.size() == 0) + actionList = null; + } + String type = section.getString("type"); + if (type == null) { + LogUtils.warn("No requirement type found at " + section.getCurrentPath()); + return EmptyRequirement.instance; + } + var builder = getRequirementFactory(type); + if (builder == null) { + return EmptyRequirement.instance; + } + return builder.build(section.get("value"), actionList, advanced); + } + + @Override + @NotNull + public Requirement getRequirement(String type, Object value) { + RequirementFactory factory = getRequirementFactory(type); + if (factory == null) { + LogUtils.warn("Requirement type: " + type + " doesn't exist."); + return EmptyRequirement.instance; + } + return factory.build(value); + } + + @Override + @Nullable + public RequirementFactory getRequirementFactory(String type) { + return requirementBuilderMap.get(type); + } + + private void registerTimeRequirement() { + registerRequirement("time", (args, actions, advanced) -> { + List> timePairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); + return state -> { + long time = state.getLocation().getWorld().getTime(); + for (Pair pair : timePairs) + if (time >= pair.left() && time <= pair.right()) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerYRequirement() { + registerRequirement("ypos", (args, actions, advanced) -> { + List> timePairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); + return state -> { + int y = state.getLocation().getBlockY(); + for (Pair pair : timePairs) + if (y >= pair.left() && y <= pair.right()) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerTemperatureRequirement() { + registerRequirement("temperature", (args, actions, advanced) -> { + List> tempPairs = ConfigUtils.stringListArgs(args).stream().map(it -> ConfigUtils.splitStringIntegerArgs(it, "~")).toList(); + return state -> { + Location location = state.getLocation(); + double temp = location.getWorld().getTemperature(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + for (Pair pair : tempPairs) + if (temp >= pair.left() && temp <= pair.right()) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerOrRequirement() { + registerRequirement("||", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + Requirement[] requirements = getRequirements(section, advanced); + return state -> { + for (Requirement requirement : requirements) { + if (requirement.isStateMet(state)) { + return true; + } + } + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at || requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerAndRequirement() { + registerRequirement("&&", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + Requirement[] requirements = getRequirements(section, advanced); + return state -> { + outer: { + for (Requirement requirement : requirements) { + if (!requirement.isStateMet(state)) { + break outer; + } + } + return true; + } + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at && requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerLevelRequirement() { + registerRequirement("level", (args, actions, advanced) -> { + int level = (int) args; + return state -> { + int current = state.getPlayer().getLevel(); + if (current >= level) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerMoneyRequirement() { + registerRequirement("money", (args, actions, advanced) -> { + double money = ConfigUtils.getDoubleValue(args); + return state -> { + double current = VaultHook.getEconomy().getBalance(state.getPlayer()); + if (current >= money) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerRandomRequirement() { + registerRequirement("random", (args, actions, advanced) -> { + double random = ConfigUtils.getDoubleValue(args); + return state -> { + if (Math.random() < random) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerBiomeRequirement() { + registerRequirement("biome", (args, actions, advanced) -> { + HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); + return state -> { + String currentBiome = BiomeAPI.getBiomeAt(state.getLocation()); + if (biomes.contains(currentBiome)) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + registerRequirement("!biome", (args, actions, advanced) -> { + HashSet biomes = new HashSet<>(ConfigUtils.stringListArgs(args)); + return state -> { + String currentBiome = BiomeAPI.getBiomeAt(state.getLocation()); + if (!biomes.contains(currentBiome)) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerWorldRequirement() { + registerRequirement("world", (args, actions, advanced) -> { + HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); + return state -> { + if (worlds.contains(state.getLocation().getWorld().getName())) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + registerRequirement("!world", (args, actions, advanced) -> { + HashSet worlds = new HashSet<>(ConfigUtils.stringListArgs(args)); + return state -> { + if (!worlds.contains(state.getLocation().getWorld().getName())) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerWeatherRequirement() { + registerRequirement("weather", (args, actions, advanced) -> { + List weathers = ConfigUtils.stringListArgs(args); + return state -> { + String currentWeather; + World world = state.getLocation().getWorld(); + if (world.hasStorm()) currentWeather = "rainstorm"; + else if (world.isThundering()) currentWeather = "thunder"; + else if (world.isClearWeather()) currentWeather = "clear"; + else currentWeather = "rain"; + for (String weather : weathers) + if (weather.equalsIgnoreCase(currentWeather)) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerCoolDownRequirement() { + registerRequirement("cooldown", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String key = section.getString("key"); + int time = section.getInt("time"); + return state -> { + if (!plugin.getCoolDownManager().isCoolDown(state.getPlayer().getUniqueId(), key, time)) { + return true; + } + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at cooldown requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerDateRequirement() { + registerRequirement("date", (args, actions, advanced) -> { + HashSet dates = new HashSet<>(ConfigUtils.stringListArgs(args)); + return state -> { + Calendar calendar = Calendar.getInstance(); + String current = (calendar.get(Calendar.MONTH) + 1) + "/" + calendar.get(Calendar.DATE); + if (dates.contains(current)) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerSneakRequirement() { + registerRequirement("sneak", (args, actions, advanced) -> { + boolean sneak = (boolean) args; + return state -> { + if (sneak) { + if (state.getPlayer().isSneaking()) + return true; + } else { + if (!state.getPlayer().isSneaking()) + return true; + } + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerPermissionRequirement() { + registerRequirement("permission", (args, actions, advanced) -> { + List perms = ConfigUtils.stringListArgs(args); + return state -> { + for (String perm : perms) + if (state.getPlayer().hasPermission(perm)) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + registerRequirement("!permission", (args, actions, advanced) -> { + List perms = ConfigUtils.stringListArgs(args); + return state -> { + for (String perm : perms) + if (state.getPlayer().hasPermission(perm)) { + if (advanced) triggerActions(actions, state); + return false; + } + return true; + }; + }); + } + + private void registerSeasonRequirement() { + registerRequirement("season", (args, actions, advanced) -> { + HashSet seasons = new HashSet<>(ConfigUtils.stringListArgs(args).stream().map(str -> str.toUpperCase(Locale.ENGLISH)).toList()); + return state -> { + Location location = state.getLocation(); + SeasonInterface seasonInterface = plugin.getIntegrationManager().getSeasonInterface(); + if (seasonInterface == null) return true; + Season season = seasonInterface.getSeason(location.getWorld()); + if (season == null) return true; + if (seasons.contains(season.name())) return true; + if (ConfigManager.enableGreenhouse()) { + for (int i = 1; i <= ConfigManager.greenhouseRange(); i++) { + if (plugin.getWorldManager().getGlassAt(SimpleLocation.of(location.clone().add(0,i,0))).isPresent()) { + return true; + } + } + } + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + @SuppressWarnings("DuplicatedCode") + private void registerGreaterThanRequirement() { + registerRequirement(">=", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (Double.parseDouble(p1) >= Double.parseDouble(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at >= requirement."); + return EmptyRequirement.instance; + } + }); + registerRequirement(">", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (Double.parseDouble(p1) > Double.parseDouble(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at > requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerRegexRequirement() { + registerRequirement("regex", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("papi", ""); + String v2 = section.getString("regex", ""); + return state -> { + if (ParseUtils.setPlaceholders(state.getPlayer(), v1).matches(v2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at regex requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerNumberEqualRequirement() { + registerRequirement("==", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (Double.parseDouble(p1) == Double.parseDouble(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at !startsWith requirement."); + return EmptyRequirement.instance; + } + }); + registerRequirement("!=", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (Double.parseDouble(p1) != Double.parseDouble(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at !startsWith requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerFertilizerRequirement() { + registerRequirement("fertilizer", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + boolean has = section.getBoolean("has"); + HashSet keys = new HashSet<>(ConfigUtils.stringListArgs(section.get("key"))); + int y = section.getInt("y", 0); + return condition -> { + Location location = condition.getLocation().clone().add(0,y,0); + SimpleLocation simpleLocation = SimpleLocation.of(location); + Optional optionalCustomCropsBlock = plugin.getWorldManager().getBlockAt(simpleLocation); + if (optionalCustomCropsBlock.isPresent()) { + if (optionalCustomCropsBlock.get() instanceof WorldPot pot) { + Fertilizer fertilizer = pot.getFertilizer(); + if (fertilizer == null) { + if (!has && keys.size() == 0) { + return true; + } + } else { + String key = fertilizer.getKey(); + if (has) { + if (keys.size() == 0) { + return true; + } + if (keys.contains(key)) { + return true; + } + } else { + if (!keys.contains(key)) { + return true; + } + } + } + } + } + if (advanced) triggerActions(actions, condition); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at fertilizer requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerItemInHandRequirement() { + registerRequirement("item-in-hand", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + int amount = section.getInt("amount", 0); + List items = ConfigUtils.stringListArgs(section.get("item")); + return condition -> { + ItemStack itemStack = condition.getItemInHand(); + if (itemStack == null) itemStack = new ItemStack(Material.AIR); + String id; + if (itemStack.getType() == Material.AIR || itemStack.getAmount() == 0) { + id = "AIR"; + } else { + id = plugin.getItemManager().getItemID(itemStack); + } + if ((items.contains(id) || items.contains("*")) && itemStack.getAmount() >= amount) return true; + if (advanced) triggerActions(actions, condition); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at item-in-hand requirement."); + return EmptyRequirement.instance; + } + }); + } + + @SuppressWarnings("DuplicatedCode") + private void registerLessThanRequirement() { + registerRequirement("<", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (Double.parseDouble(p1) < Double.parseDouble(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at < requirement."); + return EmptyRequirement.instance; + } + }); + registerRequirement("<=", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (Double.parseDouble(p1) <= Double.parseDouble(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at <= requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerStartWithRequirement() { + registerRequirement("startsWith", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (p1.startsWith(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at startsWith requirement."); + return EmptyRequirement.instance; + } + }); + registerRequirement("!startsWith", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (!p1.startsWith(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at !startsWith requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerEndWithRequirement() { + registerRequirement("endsWith", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (p1.endsWith(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at endsWith requirement."); + return EmptyRequirement.instance; + } + }); + registerRequirement("!endsWith", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (!p1.endsWith(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at !endsWith requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerContainRequirement() { + registerRequirement("contains", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (p1.contains(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at contains requirement."); + return EmptyRequirement.instance; + } + }); + registerRequirement("!contains", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (!p1.contains(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at !contains requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerInListRequirement() { + registerRequirement("in-list", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String papi = section.getString("papi", ""); + HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); + return state -> { + String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), papi) : papi; + if (values.contains(p1)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at in-list requirement."); + return EmptyRequirement.instance; + } + }); + registerRequirement("!in-list", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String papi = section.getString("papi", ""); + HashSet values = new HashSet<>(ConfigUtils.stringListArgs(section.get("values"))); + return state -> { + String p1 = papi.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), papi) : papi; + if (!values.contains(p1)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at in-list requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerEqualsRequirement() { + registerRequirement("equals", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (p1.equals(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at equals requirement."); + return EmptyRequirement.instance; + } + }); + registerRequirement("!equals", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String v1 = section.getString("value1", ""); + String v2 = section.getString("value2", ""); + return state -> { + String p1 = v1.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v1) : v1; + String p2 = v2.startsWith("%") ? ParseUtils.setPlaceholders(state.getPlayer(), v2) : v2; + if (!p1.equals(p2)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at !equals requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerEnvironmentRequirement() { + registerRequirement("environment", (args, actions, advanced) -> { + List environments = ConfigUtils.stringListArgs(args); + return state -> { + var name = state.getLocation().getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); + if (environments.contains(name)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + registerRequirement("!environment", (args, actions, advanced) -> { + List environments = ConfigUtils.stringListArgs(args); + return state -> { + var name = state.getLocation().getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH); + if (!environments.contains(name)) return true; + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void registerPluginLevelRequirement() { + registerRequirement("plugin-level", (args, actions, advanced) -> { + if (args instanceof ConfigurationSection section) { + String pluginName = section.getString("plugin"); + int level = section.getInt("level"); + String target = section.getString("target"); + return state -> { + LevelInterface levelInterface = plugin.getIntegrationManager().getLevelPlugin(pluginName); + if (levelInterface == null) { + LogUtils.warn("Plugin (" + pluginName + "'s) level is not compatible. Please double check if it's a problem caused by pronunciation."); + return true; + } + if (levelInterface.getLevel(state.getPlayer(), target) >= level) + return true; + if (advanced) triggerActions(actions, state); + return false; + }; + } else { + LogUtils.warn("Wrong value format found at plugin-level requirement."); + return EmptyRequirement.instance; + } + }); + } + + private void registerPotionEffectRequirement() { + registerRequirement("potion-effect", (args, actions, advanced) -> { + String potions = (String) args; + String[] split = potions.split("(<=|>=|<|>|==)", 2); + PotionEffectType type = PotionEffectType.getByName(split[0]); + if (type == null) { + LogUtils.warn("Potion effect doesn't exist: " + split[0]); + return EmptyRequirement.instance; + } + int required = Integer.parseInt(split[1]); + String operator = potions.substring(split[0].length(), potions.length() - split[1].length()); + return state -> { + int level = -1; + PotionEffect potionEffect = state.getPlayer().getPotionEffect(type); + if (potionEffect != null) { + level = potionEffect.getAmplifier(); + } + boolean result = false; + switch (operator) { + case ">=" -> { + if (level >= required) result = true; + } + case ">" -> { + if (level > required) result = true; + } + case "==" -> { + if (level == required) result = true; + } + case "!=" -> { + if (level != required) result = true; + } + case "<=" -> { + if (level <= required) result = true; + } + case "<" -> { + if (level < required) result = true; + } + } + if (result) { + return true; + } + if (advanced) triggerActions(actions, state); + return false; + }; + }); + } + + private void triggerActions(List actions, State state) { + if (actions != null) + for (Action action : actions) + action.trigger(state); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private void loadExpansions() { + File expansionFolder = new File(plugin.getDataFolder(), EXPANSION_FOLDER); + if (!expansionFolder.exists()) + expansionFolder.mkdirs(); + + List> classes = new ArrayList<>(); + File[] expansionJars = expansionFolder.listFiles(); + if (expansionJars == null) return; + for (File expansionJar : expansionJars) { + if (expansionJar.getName().endsWith(".jar")) { + try { + Class expansionClass = ClassUtils.findClass(expansionJar, RequirementExpansion.class); + classes.add(expansionClass); + } catch (IOException | ClassNotFoundException e) { + LogUtils.warn("Failed to load expansion: " + expansionJar.getName(), e); + } + } + } + try { + for (Class expansionClass : classes) { + RequirementExpansion expansion = expansionClass.getDeclaredConstructor().newInstance(); + unregisterRequirement(expansion.getRequirementType()); + registerRequirement(expansion.getRequirementType(), expansion.getRequirementFactory()); + LogUtils.info("Loaded requirement expansion: " + expansion.getRequirementType() + "[" + expansion.getVersion() + "]" + " by " + expansion.getAuthor()); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + LogUtils.warn("Error occurred when creating expansion instance.", e); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java new file mode 100644 index 0000000..1a4b29b --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CChunk.java @@ -0,0 +1,546 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.ChunkPos; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.*; +import net.momirealms.customcrops.mechanic.world.block.MemoryPot; +import net.momirealms.customcrops.mechanic.world.block.MemorySprinkler; +import net.momirealms.customcrops.scheduler.task.TickTask; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; + +public class CChunk implements CustomCropsChunk { + + private transient CWorld cWorld; + private final ChunkCoordinate chunkCoordinate; + private final ConcurrentHashMap loadedSections; + private final PriorityQueue queue; + private final Set tickedBlocks; + private long lastLoadedTime; + private int loadedSeconds; + private int unloadedSeconds; + + public CChunk(CWorld cWorld, ChunkCoordinate chunkCoordinate) { + this.cWorld = cWorld; + this.chunkCoordinate = chunkCoordinate; + this.loadedSections = new ConcurrentHashMap<>(64); + this.queue = new PriorityQueue<>(); + this.unloadedSeconds = 0; + this.tickedBlocks = Collections.synchronizedSet(new HashSet<>()); + this.updateLastLoadedTime(); + } + + public CChunk( + CWorld cWorld, + ChunkCoordinate chunkCoordinate, + int loadedSeconds, + long lastLoadedTime, + ConcurrentHashMap loadedSections, + PriorityQueue queue, + HashSet tickedBlocks + ) { + this.cWorld = cWorld; + this.chunkCoordinate = chunkCoordinate; + this.loadedSections = loadedSections; + this.lastLoadedTime = lastLoadedTime; + this.loadedSeconds = loadedSeconds; + this.queue = queue; + this.unloadedSeconds = 0; + this.tickedBlocks = Collections.synchronizedSet(tickedBlocks); + } + + @Override + public void updateLastLoadedTime() { + this.lastLoadedTime = System.currentTimeMillis(); + } + + @Override + public void notifyOfflineUpdates() { + long delta = this.lastLoadedTime - System.currentTimeMillis(); + int seconds = (int) (delta / 1000); + + } + + public void setWorld(CWorld cWorld) { + this.cWorld = cWorld; + } + + @Override + public CustomCropsWorld getCustomCropsWorld() { + return cWorld; + } + + @Override + public ChunkCoordinate getChunkCoordinate() { + return chunkCoordinate; + } + + @Override + public void secondTimer() { + WorldSetting setting = cWorld.getWorldSetting(); + int interval = setting.getMinTickUnit(); + this.loadedSeconds++; + // if loadedSeconds reach another recycle, rearrange the tasks + if (this.loadedSeconds >= interval) { + this.loadedSeconds = 0; + this.tickedBlocks.clear(); + this.queue.clear(); + if (setting.isScheduledTick()) { + this.arrangeTasks(setting.getMinTickUnit()); + } + } + + // scheduled tick + while (!queue.isEmpty() && queue.peek().getTime() <= loadedSeconds) { + TickTask task = queue.poll(); + if (task != null) { + ChunkPos pos = task.getChunkPos(); + CSection section = loadedSections.get(pos.getSectionID()); + if (section != null) { + CustomCropsBlock block = section.getBlockAt(pos); + if (block == null) continue; + switch (block.getType()) { + case SCARECROW, GREENHOUSE -> {} + case POT -> { + if (!setting.randomTickPot()) { + block.tick(setting.getTickPotInterval()); + } + } + case CROP -> { + if (!setting.randomTickCrop()) { + block.tick(setting.getTickCropInterval()); + } + } + case SPRINKLER -> { + if (!setting.randomTickSprinkler()) { + block.tick(setting.getTickSprinklerInterval()); + } + } + } + } + } + } + + // random tick + ThreadLocalRandom random = ThreadLocalRandom.current(); + int randomTicks = setting.getRandomTickSpeed(); + for (CustomCropsSection section : getSections()) { + int sectionID = section.getSectionID(); + int baseY = sectionID * 16; + for (int i = 0; i < randomTicks; i++) { + int x = random.nextInt(16); + int y = random.nextInt(16) + baseY; + int z = random.nextInt(16); + CustomCropsBlock block = section.getBlockAt(new ChunkPos(x,y,z)); + if (block != null) { + switch (block.getType()) { + case CROP -> { + if (setting.randomTickCrop()) { + block.tick(setting.getTickCropInterval()); + } + } + case SPRINKLER -> { + if (setting.randomTickSprinkler()) { + block.tick(setting.getTickSprinklerInterval()); + } + } + case POT -> { + ((WorldPot) block).tickWater(this); + if (setting.randomTickPot()) { + block.tick(setting.getTickPotInterval()); + } + } + } + } + } + } + } + + @Override + public long getLastLoadedTime() { + return lastLoadedTime; + } + + @Override + public int getLoadedSeconds() { + return this.loadedSeconds; + } + + public void arrangeTasks(int unit) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for (CustomCropsSection section : getSections()) { + for (Map.Entry entry : section.getBlockMap().entrySet()) { + this.queue.add(new TickTask( + random.nextInt(0, unit), + entry.getKey() + )); + this.tickedBlocks.add(entry.getKey()); + } + } + } + + public void tryCreatingTaskForNewBlock(ChunkPos pos) { + WorldSetting setting = cWorld.getWorldSetting(); + if (setting.isScheduledTick() && !tickedBlocks.contains(pos)) { + tickedBlocks.add(pos); + int random = ThreadLocalRandom.current().nextInt(0, setting.getMinTickUnit()); + if (random > loadedSeconds) { + queue.add(new TickTask(random, pos)); + } + } + } + + @Override + public Optional getCropAt(SimpleLocation simpleLocation) { + return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldCrop worldCrop ? worldCrop : null); + } + + @Override + public Optional getSprinklerAt(SimpleLocation simpleLocation) { + return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldSprinkler worldSprinkler ? worldSprinkler : null); + } + + @Override + public Optional getPotAt(SimpleLocation simpleLocation) { + return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldPot worldPot ? worldPot : null); + } + + @Override + public Optional getGlassAt(SimpleLocation simpleLocation) { + return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldGlass worldGlass ? worldGlass : null); + } + + @Override + public Optional getScarecrowAt(SimpleLocation simpleLocation) { + return getBlockAt(simpleLocation).map(customCropsBlock -> customCropsBlock instanceof WorldScarecrow worldScarecrow ? worldScarecrow : null); + } + + @Override + public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount) { + Optional optionalSprinkler = getSprinklerAt(location); + if (optionalSprinkler.isEmpty()) { + addBlockAt(new MemorySprinkler(location, sprinkler.getKey(), amount), location); + CustomCropsPlugin.get().debug("When adding water to sprinkler at " + location + ", the sprinkler data doesn't exist."); + if (sprinkler.get3DItemWithWater() != null) { + CustomCropsPlugin.get().getItemManager().removeAnythingAt(location.getBukkitLocation()); + CustomCropsPlugin.get().getItemManager().placeItem(location.getBukkitLocation(), sprinkler.getItemCarrier(), sprinkler.get3DItemWithWater()); + } + } else { + int current = optionalSprinkler.get().getWater(); + if (current == 0) { + if (sprinkler.get3DItemWithWater() != null) { + CustomCropsPlugin.get().getItemManager().removeAnythingAt(location.getBukkitLocation()); + CustomCropsPlugin.get().getItemManager().placeItem(location.getBukkitLocation(), sprinkler.getItemCarrier(), sprinkler.get3DItemWithWater()); + } + } + optionalSprinkler.get().setWater(current + amount); + } + } + + @Override + public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location) { + Optional optionalWorldPot = getPotAt(location); + if (optionalWorldPot.isEmpty()) { + MemoryPot memoryPot = new MemoryPot(location, pot.getKey()); + memoryPot.setFertilizer(fertilizer); + addBlockAt(memoryPot, location); + CustomCropsPlugin.get().debug("When adding fertilizer to pot at " + location + ", the pot data doesn't exist."); + } else { + optionalWorldPot.get().setFertilizer(fertilizer); + } + } + + @Override + public void addWaterToPot(Pot pot, SimpleLocation location, int amount) { + Optional optionalWorldPot = getPotAt(location); + if (optionalWorldPot.isEmpty()) { + MemoryPot memoryPot = new MemoryPot(location, pot.getKey()); + memoryPot.setWater(amount); + addBlockAt(memoryPot, location); + CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, true, null); + CustomCropsPlugin.get().debug("When adding water to pot at " + location + ", the pot data doesn't exist."); + } else { + optionalWorldPot.get().setWater(optionalWorldPot.get().getWater() + amount); + CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, true, optionalWorldPot.get().getFertilizer()); + } + } + + @Override + public void addPotAt(WorldPot pot, SimpleLocation location) { + CustomCropsBlock previous = addBlockAt(pot, location); + if (previous != null) { + if (previous instanceof WorldPot) { + CustomCropsPlugin.get().debug("Found duplicated pot data when adding pot at " + location); + } else { + CustomCropsPlugin.get().debug("Found unremoved data when adding crop at " + location + ". Previous type is " + previous.getType().name()); + } + } + } + + @Override + public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) { + CustomCropsBlock previous = addBlockAt(sprinkler, location); + if (previous != null) { + if (previous instanceof WorldSprinkler) { + CustomCropsPlugin.get().debug("Found duplicated sprinkler data when adding sprinkler at " + location); + } else { + CustomCropsPlugin.get().debug("Found unremoved data when adding crop at " + location + ". Previous type is " + previous.getType().name()); + } + } + } + + @Override + public void addCropAt(WorldCrop crop, SimpleLocation location) { + CustomCropsBlock previous = addBlockAt(crop, location); + if (previous != null) { + if (previous instanceof WorldCrop) { + CustomCropsPlugin.get().debug("Found duplicated crop data when adding crop at " + location); + } else { + CustomCropsPlugin.get().debug("Found unremoved data when adding crop at " + location + ". Previous type is " + previous.getType().name()); + } + } + } + + @Override + public void addPointToCrop(Crop crop, SimpleLocation location, int points) { + if (points <= 0) return; + Optional cropData = getCropAt(location); + if (cropData.isEmpty()) { + return; + } + WorldCrop worldCrop = cropData.get(); + int previousPoint = worldCrop.getPoint(); + int x = Math.min(previousPoint + points, crop.getMaxPoints()); + worldCrop.setPoint(x); + Location bkLoc = location.getBukkitLocation(); + if (bkLoc == null) return; + for (int i = previousPoint + 1; i <= x; i++) { + Crop.Stage stage = crop.getStageByPoint(i); + if (stage != null) { + stage.trigger(ActionTrigger.GROW, new State(null, new ItemStack(Material.AIR), bkLoc)); + } + } + String pre = crop.getStageItemByPoint(previousPoint); + String after = crop.getStageItemByPoint(x); + if (pre.equals(after)) return; + CustomCropsPlugin.get().getItemManager().removeAnythingAt(bkLoc); + CustomCropsPlugin.get().getItemManager().placeItem(bkLoc, crop.getItemCarrier(), after); + } + + @Override + public void addGlassAt(WorldGlass glass, SimpleLocation location) { + CustomCropsBlock previous = addBlockAt(glass, location); + if (previous != null) { + if (previous instanceof WorldGlass) { + CustomCropsPlugin.get().debug("Found duplicated glass data when adding crop at " + location); + } else { + CustomCropsPlugin.get().debug("Found unremoved data when adding glass at " + location + ". Previous type is " + previous.getType().name()); + } + } + } + + @Override + public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { + CustomCropsBlock previous = addBlockAt(scarecrow, location); + if (previous != null) { + if (previous instanceof WorldScarecrow) { + CustomCropsPlugin.get().debug("Found duplicated glass data when adding scarecrow at " + location); + } else { + CustomCropsPlugin.get().debug("Found unremoved data when adding scarecrow at " + location + ". Previous type is " + previous.getType().name()); + } + } + } + + @Override + public void removeSprinklerAt(SimpleLocation location) { + CustomCropsBlock removed = removeBlockAt(location); + if (removed == null) { + CustomCropsPlugin.get().debug("Failed to remove sprinkler from " + location + " because sprinkler doesn't exist."); + } else if (!(removed instanceof WorldSprinkler)) { + CustomCropsPlugin.get().debug("Removed sprinkler from " + location + " but the previous block type is " + removed.getType().name()); + } + } + + @Override + public void removePotAt(SimpleLocation location) { + CustomCropsBlock removed = removeBlockAt(location); + if (removed == null) { + CustomCropsPlugin.get().debug("Failed to remove pot from " + location + " because pot doesn't exist."); + } else if (!(removed instanceof WorldPot)) { + CustomCropsPlugin.get().debug("Removed pot from " + location + " but the previous block type is " + removed.getType().name()); + } + } + + @Override + public void removeCropAt(SimpleLocation location) { + CustomCropsBlock removed = removeBlockAt(location); + if (removed == null) { + CustomCropsPlugin.get().debug("Failed to remove crop from " + location + " because crop doesn't exist."); + } else if (!(removed instanceof WorldCrop)) { + CustomCropsPlugin.get().debug("Removed crop from " + location + " but the previous block type is " + removed.getType().name()); + } + } + + @Override + public void removeGlassAt(SimpleLocation location) { + CustomCropsBlock removed = removeBlockAt(location); + if (removed == null) { + CustomCropsPlugin.get().debug("Failed to remove glass from " + location + " because glass doesn't exist."); + } else if (!(removed instanceof WorldGlass)) { + CustomCropsPlugin.get().debug("Removed glass from " + location + " but the previous block type is " + removed.getType().name()); + } + } + + @Override + public void removeScarecrowAt(SimpleLocation location) { + CustomCropsBlock removed = removeBlockAt(location); + if (removed == null) { + CustomCropsPlugin.get().debug("Failed to remove scarecrow from " + location + " because scarecrow doesn't exist."); + } else if (!(removed instanceof WorldScarecrow)) { + CustomCropsPlugin.get().debug("Removed scarecrow from " + location + " but the previous block type is " + removed.getType().name()); + } + } + + @Override + public CustomCropsBlock removeBlockAt(SimpleLocation location) { + ChunkPos pos = ChunkPos.getByLocation(location); + CSection section = loadedSections.get(pos.getSectionID()); + if (section == null) return null; + return section.removeBlockAt(pos); + } + + @Override + public CustomCropsBlock addBlockAt(CustomCropsBlock block, SimpleLocation location) { + ChunkPos pos = ChunkPos.getByLocation(location); + CSection section = loadedSections.get(pos.getSectionID()); + if (section == null) { + section = new CSection(pos.getSectionID()); + loadedSections.put(pos.getSectionID(), section); + } + this.tryCreatingTaskForNewBlock(pos); + return section.addBlockAt(pos, block); + } + + @Override + public Optional getBlockAt(SimpleLocation location) { + ChunkPos pos = ChunkPos.getByLocation(location); + CSection section = loadedSections.get(pos.getSectionID()); + if (section == null) { + return Optional.empty(); + } + return Optional.ofNullable(section.getBlockAt(pos)); + } + + @Override + public int getCropAmount() { + int amount = 0; + for (CustomCropsSection section : getSections()) { + for (CustomCropsBlock block : section.getBlocks()) { + if (block instanceof WorldCrop) { + amount++; + } + } + } + return amount; + } + + @Override + public int getPotAmount() { + int amount = 0; + for (CustomCropsSection section : getSections()) { + for (CustomCropsBlock block : section.getBlocks()) { + if (block instanceof WorldPot) { + amount++; + } + } + } + return amount; + } + + @Override + public int getSprinklerAmount() { + int amount = 0; + for (CustomCropsSection section : getSections()) { + for (CustomCropsBlock block : section.getBlocks()) { + if (block instanceof WorldSprinkler) { + amount++; + } + } + } + return amount; + } + + public CSection[] getSectionsForSerialization() { + ArrayList sections = new ArrayList<>(); + for (Map.Entry entry : loadedSections.entrySet()) { + if (!entry.getValue().canPrune()) { + sections.add(entry.getValue()); + } + } + return sections.toArray(new CSection[0]); + } + + @Override + public CustomCropsSection[] getSections() { + return loadedSections.values().toArray(new CustomCropsSection[0]); + } + + @Override + public CustomCropsSection getSection(int sectionID) { + return loadedSections.get(sectionID); + } + + @Override + public int getUnloadedSeconds() { + return unloadedSeconds; + } + + @Override + public void setUnloadedSeconds(int unloadedSeconds) { + this.unloadedSeconds = unloadedSeconds; + } + + @Override + public boolean canPrune() { + return loadedSections.size() == 0; + } + + public PriorityQueue getQueue() { + return queue; + } + + public Set getTickedBlocks() { + return tickedBlocks; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CSection.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CSection.java new file mode 100644 index 0000000..804d1c7 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CSection.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world; + +import net.momirealms.customcrops.api.mechanic.world.ChunkPos; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsSection; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class CSection implements CustomCropsSection { + + private final int sectionID; + private final ConcurrentHashMap blocks; + + public CSection(int sectionID) { + this.sectionID = sectionID; + this.blocks = new ConcurrentHashMap<>(); + } + + public CSection(int sectionID, ConcurrentHashMap blocks) { + this.blocks = blocks; + this.sectionID = sectionID; + } + + @Override + public int getSectionID() { + return sectionID; + } + + @Override + public CustomCropsBlock getBlockAt(ChunkPos pos) { + return blocks.get(pos); + } + + @Override + public CustomCropsBlock removeBlockAt(ChunkPos pos) { + return blocks.remove(pos); + } + + @Override + public CustomCropsBlock addBlockAt(ChunkPos pos, CustomCropsBlock block) { + return blocks.put(pos, block); + } + + @Override + public boolean canPrune() { + return blocks.size() == 0; + } + + @Override + public CustomCropsBlock[] getBlocks() { + return blocks.values().toArray(new CustomCropsBlock[0]); + } + + @Override + public Map getBlockMap() { + return blocks; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java new file mode 100644 index 0000000..5d59a66 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/CWorld.java @@ -0,0 +1,470 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.event.SeasonChangeEvent; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.manager.WorldManager; +import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.*; +import net.momirealms.customcrops.api.mechanic.world.season.Season; +import net.momirealms.customcrops.api.scheduler.CancellableTask; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.utils.EventUtils; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class CWorld implements CustomCropsWorld { + + private final WorldManager worldManager; + private final WeakReference world; + private final ConcurrentHashMap loadedChunks; + private final ConcurrentHashMap lazyChunks; + private WorldSetting setting; + private WorldInfoData infoData; + private final String worldName; + private CancellableTask worldTask; + private int currentMinecraftDay; + + public CWorld(WorldManager worldManager, World world) { + this.world = new WeakReference<>(world); + this.worldManager = worldManager; + this.loadedChunks = new ConcurrentHashMap<>(); + this.lazyChunks = new ConcurrentHashMap<>(); + this.worldName = world.getName(); + this.currentMinecraftDay = (int) (world.getFullTime() / 24000); + } + + @Override + public void startTick() { + if (this.worldTask == null || this.worldTask.isCancelled()) + this.worldTask = CustomCropsPlugin.get().getScheduler().runTaskAsyncTimer(this::timer, 1, 1, TimeUnit.SECONDS); + } + + @Override + public void cancelTick() { + if (!this.worldTask.isCancelled()) + this.worldTask.cancel(); + } + + private void timer() { + ArrayList> chunksToSave = new ArrayList<>(); + for (Map.Entry lazyEntry : lazyChunks.entrySet()) { + CChunk chunk = lazyEntry.getValue(); + int sec = chunk.getUnloadedSeconds() + 1; + if (sec >= 10) { + chunksToSave.add(Pair.of(lazyEntry.getKey(), chunk)); + } else { + chunk.setUnloadedSeconds(sec); + } + } + for (Pair pair : chunksToSave) { + lazyChunks.remove(pair.left()); + worldManager.saveChunkToFile(pair.right()); + } + if (setting.isAutoSeasonChange()) { + this.updateSeasonAndDate(); + } + for (CChunk chunk : loadedChunks.values()) { + chunk.secondTimer(); + } + } + + private void updateSeasonAndDate() { + World bukkitWorld = world.get(); + if (bukkitWorld == null) { + LogUtils.severe(String.format("World %s unloaded unexpectedly. Stop ticking task...", worldName)); + this.cancelTick(); + return; + } + + long ticks = bukkitWorld.getFullTime(); + int days = (int) (ticks / 24000); + if (days == this.currentMinecraftDay) { + return; + } + + if (days > this.currentMinecraftDay) { + int date = infoData.getDate(); + date++; + if (date > setting.getSeasonDuration()) { + date = 1; + Season next = infoData.getSeason().getNextSeason(); + infoData.setSeason(next); + EventUtils.fireAndForget(new SeasonChangeEvent(bukkitWorld, next)); + } + infoData.setDate(date); + } + this.currentMinecraftDay = days; + } + + @Override + public CustomCropsChunk removeLazyChunkAt(ChunkCoordinate chunkCoordinate) { + return lazyChunks.remove(chunkCoordinate); + } + + @Override + public WorldSetting getWorldSetting() { + return setting; + } + + @Override + public void setWorldSetting(WorldSetting setting) { + this.setting = setting; + } + + @Override + public Collection getChunkStorage() { + return loadedChunks.values(); + } + + @Nullable + @Override + public World getWorld() { + return world.get(); + } + + @Override + public String getWorldName() { + return worldName; + } + + @Override + public boolean isChunkLoaded(ChunkCoordinate chunkCoordinate) { + return loadedChunks.containsKey(chunkCoordinate); + } + + @Override + public Optional getChunkAt(ChunkCoordinate chunkCoordinate) { + return Optional.ofNullable(createOrGetChunk(chunkCoordinate)); + } + + @Override + public void loadChunk(CustomCropsChunk chunk) { + ChunkCoordinate chunkCoordinate = chunk.getChunkCoordinate(); + if (loadedChunks.containsKey(chunkCoordinate)) { + LogUtils.warn("Invalid operation: Loaded chunk is loaded again." + chunkCoordinate); + return; + } + loadedChunks.put(chunkCoordinate, (CChunk) chunk); + } + + @Override + public void unloadChunk(ChunkCoordinate chunkCoordinate) { + CChunk chunk = loadedChunks.remove(chunkCoordinate); + if (chunk != null) { + chunk.updateLastLoadedTime(); + lazyChunks.put(chunkCoordinate, chunk); + } + } + + @Override + public void setInfoData(WorldInfoData infoData) { + this.infoData = infoData; + } + + @Override + public WorldInfoData getInfoData() { + return infoData; + } + + @Override + @Nullable + public Season getSeason() { + if (setting.isEnableSeason()) { + if (ConfigManager.syncSeasons() && ConfigManager.referenceWorld() != world) { + return worldManager.getCustomCropsWorld(ConfigManager.referenceWorld()).map(customCropsWorld -> customCropsWorld.getInfoData().getSeason()).orElse(null); + } + return infoData.getSeason(); + } else { + return null; + } + } + + @Override + public Optional getSprinklerAt(SimpleLocation location) { + CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + if (chunk == null) return Optional.empty(); + return chunk.getSprinklerAt(location); + } + + @Override + public Optional getPotAt(SimpleLocation location) { + CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + if (chunk == null) return Optional.empty(); + return chunk.getPotAt(location); + } + + @Override + public Optional getCropAt(SimpleLocation location) { + CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + if (chunk == null) return Optional.empty(); + return chunk.getCropAt(location); + } + + @Override + public Optional getGlassAt(SimpleLocation location) { + CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + if (chunk == null) return Optional.empty(); + return chunk.getGlassAt(location); + } + + @Override + public Optional getScarecrowAt(SimpleLocation location) { + CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + if (chunk == null) return Optional.empty(); + return chunk.getScarecrowAt(location); + } + + @Override + public Optional getBlockAt(SimpleLocation location) { + CChunk chunk = loadedChunks.get(location.getChunkCoordinate()); + if (chunk == null) return Optional.empty(); + return chunk.getBlockAt(location); + } + + @Override + public void addWaterToSprinkler(Sprinkler sprinkler, SimpleLocation location, int amount) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addWaterToSprinkler(sprinkler, location, amount); + } else { + LogUtils.warn("Invalid operation: Adding water to sprinkler in a not generated chunk"); + } + } + + @Override + public void addFertilizerToPot(Pot pot, Fertilizer fertilizer, SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addFertilizerToPot(pot, fertilizer, location); + } else { + LogUtils.warn("Invalid operation: Adding fertilizer to pot in a not generated chunk"); + } + } + + @Override + public void addWaterToPot(Pot pot, SimpleLocation location, int amount) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addWaterToPot(pot, location, amount); + } else { + LogUtils.warn("Invalid operation: Adding water to pot in a not generated chunk"); + } + } + + @Override + public void addPotAt(WorldPot pot, SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addPotAt(pot, location); + } else { + LogUtils.warn("Invalid operation: Adding pot in a not generated chunk"); + } + } + + @Override + public void addSprinklerAt(WorldSprinkler sprinkler, SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addSprinklerAt(sprinkler, location); + } else { + LogUtils.warn("Invalid operation: Adding sprinkler in a not generated chunk"); + } + } + + @Override + public void addCropAt(WorldCrop crop, SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addCropAt(crop, location); + } else { + LogUtils.warn("Invalid operation: Adding crop in a not generated chunk"); + } + } + + @Override + public void addPointToCrop(Crop crop, SimpleLocation location, int points) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addPointToCrop(crop, location, points); + } else { + LogUtils.warn("Invalid operation: Adding points to crop in a not generated chunk"); + } + } + + @Override + public void addGlassAt(WorldGlass glass, SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addGlassAt(glass, location); + } else { + LogUtils.warn("Invalid operation: Adding glass in a not generated chunk"); + } + } + + @Override + public void addScarecrowAt(WorldScarecrow scarecrow, SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().addScarecrowAt(scarecrow, location); + } else { + LogUtils.warn("Invalid operation: Adding scarecrow in a not generated chunk"); + } + } + + @Override + public void removeSprinklerAt(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().removeSprinklerAt(location); + } else { + LogUtils.warn("Invalid operation: Removing sprinkler from a not generated chunk"); + } + } + + @Override + public void removePotAt(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().removePotAt(location); + } else { + LogUtils.warn("Invalid operation: Removing pot from a not generated chunk"); + } + } + + @Override + public void removeCropAt(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().removeCropAt(location); + } else { + LogUtils.warn("Invalid operation: Removing crop from a not generated chunk"); + } + } + + @Override + public void removeGlassAt(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().removeGlassAt(location); + } else { + LogUtils.warn("Invalid operation: Removing glass from a not generated chunk"); + } + } + + @Override + public void removeScarecrowAt(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + chunk.get().removeScarecrowAt(location); + } else { + LogUtils.warn("Invalid operation: Removing scarecrow from a not generated chunk"); + } + } + + @Override + public CustomCropsBlock removeAnythingAt(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isPresent()) { + return chunk.get().removeBlockAt(location); + } else { + LogUtils.warn("Invalid operation: Removing anything from a not generated chunk"); + return null; + } + } + + @Nullable + private CustomCropsChunk createOrGetChunk(ChunkCoordinate chunkCoordinate) { + World bukkitWorld = world.get(); + if (bukkitWorld == null) + return null; + CChunk chunk = loadedChunks.get(chunkCoordinate); + if (chunk != null) { + return chunk; + } + if (bukkitWorld.isChunkLoaded(chunkCoordinate.x(), chunkCoordinate.z())) { + chunk = new CChunk(this, chunkCoordinate); + loadChunk(chunk); + return chunk; + } else { + if (bukkitWorld.isChunkGenerated(chunkCoordinate.x(), chunkCoordinate.z())) { + Chunk bukkitChunk = bukkitWorld.getChunkAt(chunkCoordinate.x(), chunkCoordinate.z()); + worldManager.handleChunkLoad(bukkitChunk); + chunk = loadedChunks.get(chunkCoordinate); + return Objects.requireNonNullElseGet(chunk, () -> new CChunk(this, chunkCoordinate)); + } else { + return null; + } + } + } + + @Override + public boolean isPotReachLimit(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isEmpty()) { + LogUtils.warn("Invalid operation: Querying pot amount from a not generated chunk"); + return true; + } + if (setting.getPotPerChunk() < 0) return false; + return chunk.get().getPotAmount() >= setting.getPotPerChunk(); + } + + @Override + public boolean isCropReachLimit(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isEmpty()) { + LogUtils.warn("Invalid operation: Querying crop amount from a not generated chunk"); + return true; + } + if (setting.getCropPerChunk() < 0) return false; + return chunk.get().getCropAmount() >= setting.getCropPerChunk(); + } + + @Override + public boolean isSprinklerReachLimit(SimpleLocation location) { + Optional chunk = getChunkAt(location.getChunkCoordinate()); + if (chunk.isEmpty()) { + LogUtils.warn("Invalid operation: Querying sprinkler amount from a not generated chunk"); + return true; + } + if (setting.getSprinklerPerChunk() < 0) return false; + return chunk.get().getSprinklerAmount() >= setting.getSprinklerPerChunk(); + } + + public Collection getAllChunksToSave() { + ArrayList chunks = new ArrayList<>(); + chunks.addAll(lazyChunks.values()); + chunks.addAll(loadedChunks.values()); + return chunks; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableChunk.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableChunk.java new file mode 100644 index 0000000..d869d75 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableChunk.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world; + +import java.util.List; + +public class SerializableChunk { + + private final int x; + private final int z; + private final int loadedSeconds; + private final long lastLoadedTime; + private final List sections; + private final int[] queuedTasks; + private final int[] ticked; + + public SerializableChunk(int x, int z, int loadedSeconds, long lastLoadedTime, List sections, int[] queuedTasks, int[] ticked) { + this.x = x; + this.z = z; + this.lastLoadedTime = lastLoadedTime; + this.loadedSeconds = loadedSeconds; + this.sections = sections; + this.queuedTasks = queuedTasks; + this.ticked = ticked; + } + + public int getLoadedSeconds() { + return loadedSeconds; + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + public long getLastLoadedTime() { + return lastLoadedTime; + } + + public List getSections() { + return sections; + } + + public int[] getQueuedTasks() { + return queuedTasks; + } + + public int[] getTicked() { + return ticked; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableSection.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableSection.java new file mode 100644 index 0000000..9184cd9 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/SerializableSection.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world; + +import com.flowpowered.nbt.CompoundTag; + +import java.util.List; + +public class SerializableSection { + + private final int sectionID; + private final List blocks; + + public SerializableSection(int sectionID, List blocks) { + this.sectionID = sectionID; + this.blocks = blocks; + } + + public int getSectionID() { + return sectionID; + } + + public List getBlocks() { + return blocks; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java new file mode 100644 index 0000000..4195859 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/WorldManagerImpl.java @@ -0,0 +1,505 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.WorldManager; +import net.momirealms.customcrops.api.mechanic.item.*; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.*; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.mechanic.world.adaptor.AbstractWorldAdaptor; +import net.momirealms.customcrops.mechanic.world.adaptor.BukkitWorldAdaptor; +import net.momirealms.customcrops.mechanic.world.adaptor.SlimeWorldAdaptor; +import net.momirealms.customcrops.utils.ConfigUtils; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +public class WorldManagerImpl implements WorldManager, Listener { + + private final CustomCropsPlugin plugin; + private final ConcurrentHashMap loadedWorlds; + private final HashMap worldSettingMap; + private WorldSetting defaultWorldSetting; + private boolean whiteListOrBlackList; + private HashSet worldList; + private AbstractWorldAdaptor worldAdaptor; + private String absoluteWorldFolder; + + public WorldManagerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.loadedWorlds = new ConcurrentHashMap<>(); + this.worldSettingMap = new HashMap<>(); + try { + Class.forName("com.infernalsuite.aswm.api.world.SlimeWorld"); + this.worldAdaptor = new SlimeWorldAdaptor(this); + } catch (ClassNotFoundException ignore) { + this.worldAdaptor = new BukkitWorldAdaptor(this); + } + } + + @Override + public void load() { + this.registerListener(); + this.loadConfig(); + if (this.worldAdaptor instanceof BukkitWorldAdaptor adaptor) { + adaptor.setWorldFolder(absoluteWorldFolder); + } + for (World world : Bukkit.getWorlds()) { + if (isMechanicEnabled(world)) { + loadWorld(world); + } else { + unloadWorld(world); + } + } + } + + @Override + public void unload() { + this.unregisterListener(); + } + + @Override + public void disable() { + this.unload(); + for (World world : Bukkit.getWorlds()) { + unloadWorld(world); + } + if (this.loadedWorlds.size() != 0) { + LogUtils.severe("Detected that some worlds are not properly unloaded. " + + "You can safely ignore this if you are editing \"worlds.list\" and restarting to apply it"); + for (String world : this.loadedWorlds.keySet()) { + LogUtils.severe(" - " + world); + } + for (CustomCropsWorld world : this.loadedWorlds.values()) { + worldAdaptor.unload(world); + } + this.loadedWorlds.clear(); + } + } + + private void loadConfig() { + YamlConfiguration config = ConfigUtils.getConfig("config.yml"); + + ConfigurationSection section = config.getConfigurationSection("worlds"); + if (section == null) { + LogUtils.severe("worlds section should not be null"); + return; + } + + this.absoluteWorldFolder = section.getString("absolute-world-folder-path",""); + this.whiteListOrBlackList = section.getString("mode", "blacklist").equalsIgnoreCase("whitelist"); + this.worldList = new HashSet<>(section.getStringList("list")); + + // limitation + ConfigurationSection settingSection = section.getConfigurationSection("settings"); + if (settingSection == null) { + LogUtils.severe("worlds.settings section should not be null"); + return; + } + + ConfigurationSection defaultSchedulerSection = settingSection.getConfigurationSection("_DEFAULT_"); + if (defaultSchedulerSection == null) { + LogUtils.severe("worlds.settings._DEFAULT_ section should not be null"); + return; + } + + this.defaultWorldSetting = ConfigUtils.getWorldSettingFromSection(defaultSchedulerSection); + } + + private void registerListener() { + Bukkit.getPluginManager().registerEvents(this, plugin); + if (worldAdaptor != null) + Bukkit.getPluginManager().registerEvents(worldAdaptor, plugin); + } + + private void unregisterListener() { + HandlerList.unregisterAll(this); + if (worldAdaptor != null) + HandlerList.unregisterAll(worldAdaptor); + } + + @NotNull + @Override + public CustomCropsWorld loadWorld(@NotNull World world) { + String worldName = world.getName(); + if (loadedWorlds.containsKey(worldName)) { + CWorld cWorld = loadedWorlds.get(worldName); + cWorld.setWorldSetting(getInitWorldSetting(world)); + return cWorld; + } + CWorld cWorld = new CWorld(this, world); + worldAdaptor.init(cWorld); + loadedWorlds.put(worldName, cWorld); + cWorld.setWorldSetting(getInitWorldSetting(world)); + cWorld.startTick(); + for (Chunk chunk : world.getLoadedChunks()) { + handleChunkLoad(chunk); + } + return cWorld; + } + + @Override + public boolean unloadWorld(@NotNull World world) { + CustomCropsWorld customCropsWorld = loadedWorlds.remove(world.getName()); + if (customCropsWorld != null) { + customCropsWorld.cancelTick(); + worldAdaptor.unload(customCropsWorld); + return true; + } + return false; + } + + private WorldSetting getInitWorldSetting(World world) { + if (worldSettingMap.containsKey(world.getName())) + return worldSettingMap.get(world.getName()).clone(); + return defaultWorldSetting.clone(); + } + + @Override + public boolean isMechanicEnabled(@NotNull World world) { + if (whiteListOrBlackList) { + return worldList.contains(world.getName()); + } else { + return !worldList.contains(world.getName()); + } + } + + @NotNull + @Override + public Collection getWorldNames() { + return loadedWorlds.keySet(); + } + + @NotNull + @Override + public Collection getBukkitWorlds() { + return loadedWorlds.keySet().stream().map(Bukkit::getWorld).toList(); + } + + @NotNull + @Override + public Collection getCustomCropsWorlds() { + return loadedWorlds.values(); + } + + @NotNull + @Override + public Optional getCustomCropsWorld(@NotNull String name) { + return Optional.ofNullable(loadedWorlds.get(name)); + } + + @NotNull + @Override + public Optional getCustomCropsWorld(@NotNull World world) { + return Optional.ofNullable(loadedWorlds.get(world.getName())); + } + + @NotNull + @Override + public Optional getSprinklerAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) return Optional.empty(); + return cWorld.getSprinklerAt(location); + } + + @NotNull + @Override + public Optional getPotAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) return Optional.empty(); + return cWorld.getPotAt(location); + } + + @NotNull + @Override + public Optional getCropAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) return Optional.empty(); + return cWorld.getCropAt(location); + } + + @NotNull + @Override + public Optional getGlassAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) return Optional.empty(); + return cWorld.getGlassAt(location); + } + + @NotNull + @Override + public Optional getScarecrowAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) return Optional.empty(); + return cWorld.getScarecrowAt(location); + } + + @Override + public Optional getBlockAt(SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) return Optional.empty(); + return cWorld.getBlockAt(location); + } + + @Override + public void addWaterToSprinkler(@NotNull Sprinkler sprinkler, @NotNull SimpleLocation location, int amount) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding water to sprinkler in unloaded world " + location); + return; + } + cWorld.addWaterToSprinkler(sprinkler, location, amount); + } + + @Override + public void addFertilizerToPot(@NotNull Pot pot, @NotNull Fertilizer fertilizer, @NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding fertilizer to pot in unloaded world " + location); + return; + } + cWorld.addFertilizerToPot(pot, fertilizer, location); + } + + @Override + public void addWaterToPot(@NotNull Pot pot, @NotNull SimpleLocation location, int amount) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding water to pot in unloaded world " + location); + return; + } + cWorld.addWaterToPot(pot, location, amount); + } + + @Override + public void addPotAt(@NotNull WorldPot pot, @NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding pot in unloaded world " + location); + return; + } + cWorld.addPotAt(pot, location); + } + + @Override + public void addSprinklerAt(@NotNull WorldSprinkler sprinkler, @NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding sprinkler in unloaded world " + location); + return; + } + cWorld.addSprinklerAt(sprinkler, location); + } + + @Override + public void addCropAt(@NotNull WorldCrop crop, @NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding crop in unloaded world " + location); + return; + } + cWorld.addCropAt(crop, location); + } + + @Override + public void addPointToCrop(@NotNull Crop crop, @NotNull SimpleLocation location, int points) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding point to crop in unloaded world " + location); + return; + } + cWorld.addPointToCrop(crop, location, points); + } + + @Override + public void addGlassAt(@NotNull WorldGlass glass, @NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding glass in unloaded world " + location); + return; + } + cWorld.addGlassAt(glass, location); + } + + @Override + public void addScarecrowAt(@NotNull WorldScarecrow scarecrow, @NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Adding scarecrow in unloaded world " + location); + return; + } + cWorld.addScarecrowAt(scarecrow, location); + } + + @Override + public void removeSprinklerAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Removing sprinkler from unloaded world " + location); + return; + } + cWorld.removeSprinklerAt(location); + } + + @Override + public void removePotAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Removing pot from unloaded world " + location); + return; + } + cWorld.removePotAt(location); + } + + @Override + public void removeCropAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Removing crop from unloaded world " + location); + return; + } + cWorld.removeCropAt(location); + } + + @Override + public void removeGlassAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Removing crop from unloaded world " + location); + return; + } + cWorld.removeGlassAt(location); + } + + @Override + public void removeScarecrowAt(@NotNull SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Removing scarecrow from unloaded world " + location); + return; + } + cWorld.removeScarecrowAt(location); + } + + @Override + public CustomCropsBlock removeAnythingAt(SimpleLocation location) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Removing anything from unloaded world " + location); + return null; + } + return cWorld.removeAnythingAt(location); + } + + @Override + public boolean isReachLimit(SimpleLocation location, ItemType itemType) { + CWorld cWorld = loadedWorlds.get(location.getWorldName()); + if (cWorld == null) { + LogUtils.warn("Unsupported operation: Querying amount in an unloaded world " + location); + return true; + } + switch (itemType) { + case CROP -> { + return cWorld.isCropReachLimit(location); + } + case SPRINKLER -> { + return cWorld.isSprinklerReachLimit(location); + } + case POT -> { + return cWorld.isPotReachLimit(location); + } + default -> { + return false; + } + } + } + + /** + * Still need further investigations into why chunk load event is called twice + */ + @Override + public void handleChunkLoad(Chunk bukkitChunk) { + Optional optional = getCustomCropsWorld(bukkitChunk.getWorld()); + if (optional.isEmpty()) + return; + CustomCropsWorld customCropsWorld = optional.get(); + ChunkCoordinate chunkCoordinate = ChunkCoordinate.getByBukkitChunk(bukkitChunk); + if (customCropsWorld.isChunkLoaded(chunkCoordinate)) { + return; + } + // load chunks + this.worldAdaptor.loadDynamicData(customCropsWorld, chunkCoordinate); + + // offline grow part + if (!customCropsWorld.getWorldSetting().isOfflineGrow()) return; + + // If chunk data not exists, return + Optional optionalChunk = customCropsWorld.getChunkAt(chunkCoordinate); + if (optionalChunk.isEmpty()) { + return; + } + + CustomCropsChunk chunk = optionalChunk.get(); + bukkitChunk.getEntities(); + chunk.notifyOfflineUpdates(); + } + + @Override + public void handleChunkUnload(Chunk bukkitChunk) { + Optional optional = getCustomCropsWorld(bukkitChunk.getWorld()); + if (optional.isEmpty()) + return; + + CustomCropsWorld customCropsWorld = optional.get(); + ChunkCoordinate chunkCoordinate = ChunkCoordinate.getByBukkitChunk(bukkitChunk); + this.worldAdaptor.unloadDynamicData(customCropsWorld, chunkCoordinate); + } + + @EventHandler + public void onChunkLoad(ChunkLoadEvent event) { + handleChunkLoad(event.getChunk()); + } + + @EventHandler + public void onChunkUnLoad(ChunkUnloadEvent event) { + handleChunkUnload(event.getChunk()); + } + + @Override + public void saveChunkToFile(CustomCropsChunk chunk) { + this.worldAdaptor.saveDynamicData(chunk.getCustomCropsWorld(), chunk); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/AbstractWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/AbstractWorldAdaptor.java new file mode 100644 index 0000000..294e404 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/AbstractWorldAdaptor.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world.adaptor; + +import net.momirealms.customcrops.api.manager.WorldManager; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; +import org.bukkit.event.Listener; + +public abstract class AbstractWorldAdaptor implements Listener { + + public static final int version = 1; + protected WorldManager worldManager; + + public AbstractWorldAdaptor(WorldManager worldManager) { + this.worldManager = worldManager; + } + + public abstract void unload(CustomCropsWorld customCropsWorld); + + public abstract void init(CustomCropsWorld customCropsWorld); + + public abstract void loadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate); + + public abstract void unloadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate); + + public abstract void saveDynamicData(CustomCropsWorld ccWorld, CustomCropsChunk chunk); +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java new file mode 100644 index 0000000..9db8e4b --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/BukkitWorldAdaptor.java @@ -0,0 +1,438 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world.adaptor; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.CompoundTag; +import com.flowpowered.nbt.IntArrayTag; +import com.flowpowered.nbt.StringTag; +import com.flowpowered.nbt.stream.NBTInputStream; +import com.flowpowered.nbt.stream.NBTOutputStream; +import com.github.luben.zstd.Zstd; +import com.google.gson.Gson; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.WorldManager; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.ChunkPos; +import net.momirealms.customcrops.api.mechanic.world.CustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; +import net.momirealms.customcrops.api.mechanic.world.level.WorldInfoData; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.mechanic.world.*; +import net.momirealms.customcrops.mechanic.world.block.*; +import net.momirealms.customcrops.scheduler.task.TickTask; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.persistence.PersistentDataType; + +import java.io.*; +import java.nio.ByteOrder; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class BukkitWorldAdaptor extends AbstractWorldAdaptor { + + private static final NamespacedKey key = new NamespacedKey(CustomCropsPlugin.get(), "data"); + private final Gson gson; + private String worldFolder; + + public BukkitWorldAdaptor(WorldManager worldManager) { + super(worldManager); + this.gson = new Gson(); + } + + @Override + public void unload(CustomCropsWorld customCropsWorld) { + CWorld cWorld = (CWorld) customCropsWorld; + World world = cWorld.getWorld(); + if (world == null) { + LogUtils.severe("Unexpected issue: World " + cWorld.getWorldName() + " unloaded before data saved"); + return; + } + + // save world data into psd + world.getPersistentDataContainer().set(key, PersistentDataType.STRING, + gson.toJson(cWorld.getInfoData())); + + new File(world.getWorldFolder(), "customcrops").mkdir(); + + for (CChunk chunk : cWorld.getAllChunksToSave()) { + saveDynamicData(cWorld, chunk); + } + } + + @Override + public void init(CustomCropsWorld customCropsWorld) { + CWorld cWorld = (CWorld) customCropsWorld; + World world = cWorld.getWorld(); + if (world == null) { + LogUtils.severe("Unexpected issue: World " + cWorld.getWorldName() + " unloaded before data loaded"); + return; + } + + // init world basic info + String json = world.getPersistentDataContainer().get(key, PersistentDataType.STRING); + WorldInfoData data = (json == null || json.equals("null")) ? WorldInfoData.empty() : gson.fromJson(json, WorldInfoData.class); + cWorld.setInfoData(data); + + new File(world.getWorldFolder(), "customcrops").mkdir(); + } + + @Override + public void loadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate) { + CWorld cWorld = (CWorld) customCropsWorld; + World world = cWorld.getWorld(); + if (world == null) { + LogUtils.severe("Unexpected issue: World " + cWorld.getWorldName() + " unloaded before data loaded"); + return; + } + + // load lazy chunks firstly + CustomCropsChunk lazyChunk = customCropsWorld.removeLazyChunkAt(chunkCoordinate); + if (lazyChunk != null) { + CChunk cChunk = (CChunk) lazyChunk; + cChunk.setUnloadedSeconds(0); + cWorld.loadChunk(cChunk); + return; + } + // create or get chunk files + File data = getChunkDataFilePath(world, chunkCoordinate); + if (!data.exists()) + return; + // load chunk from local files + long time1 = System.currentTimeMillis(); + try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(getChunkDataFilePath(world, chunkCoordinate)))) { + DataInputStream dataStream = new DataInputStream(bis); + CChunk chunk = deserialize(cWorld, dataStream); + dataStream.close(); + cWorld.loadChunk(chunk); + long time2 = System.currentTimeMillis(); + CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to load chunk " + chunkCoordinate); + } catch (IOException e) { + LogUtils.severe("Failed to load CustomCrops data at " + chunkCoordinate); + e.printStackTrace(); + } + } + + @Override + public void unloadDynamicData(CustomCropsWorld ccWorld, ChunkCoordinate chunkCoordinate) { + CWorld cWorld = (CWorld) ccWorld; + World world = cWorld.getWorld(); + if (world == null) { + LogUtils.severe("Unexpected issue: World " + cWorld.getWorldName() + " unloaded before data loaded"); + return; + } + + cWorld.unloadChunk(chunkCoordinate); + } + + @Override + public void saveDynamicData(CustomCropsWorld ccWorld, CustomCropsChunk chunk) { + long time1 = System.currentTimeMillis(); + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(getChunkDataFilePath(ccWorld.getWorld(), chunk.getChunkCoordinate())))) { + bos.write(serialize((CChunk) chunk)); + long time2 = System.currentTimeMillis(); + CustomCropsPlugin.get().debug("Took " + (time2-time1) + "ms to save chunk " + chunk.getChunkCoordinate()); + } catch (IOException e) { + LogUtils.severe("Failed to save CustomCrops data."); + e.printStackTrace(); + } + } + + @EventHandler(ignoreCancelled = true) + public void onWorldLoad(WorldLoadEvent event) { + if (worldManager.isMechanicEnabled(event.getWorld())) { + worldManager.loadWorld(event.getWorld());; + } + } + + @EventHandler (ignoreCancelled = true) + public void onWorldUnload(WorldUnloadEvent event) { + if (worldManager.isMechanicEnabled(event.getWorld())) + worldManager.unloadWorld(event.getWorld()); + } + + private String getChunkDataFile(ChunkCoordinate chunkCoordinate) { + return chunkCoordinate.x() + "," + chunkCoordinate.z() + ".ccd"; + } + + private File getChunkDataFilePath(World world, ChunkCoordinate chunkCoordinate) { + if (worldFolder.isEmpty()) { + return new File(world.getWorldFolder(), "customcrops" + File.separator + getChunkDataFile(chunkCoordinate)); + } else { + return new File(worldFolder, world.getName() + File.separator + "customcrops" + File.separator + getChunkDataFile(chunkCoordinate)); + } + } + + public void setWorldFolder(String folder) { + this.worldFolder = folder; + } + + public byte[] serialize(CChunk chunk) { + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); + DataOutputStream outStream = new DataOutputStream(outByteStream); + SerializableChunk serializableChunk = toSerializableChunk(chunk); + try { + outStream.writeByte(version); + byte[] serializedSections = serializeChunk(serializableChunk); + byte[] compressed = Zstd.compress(serializedSections); + outStream.writeInt(compressed.length); + outStream.writeInt(serializedSections.length); + outStream.write(compressed); + } catch (IOException e) { + e.printStackTrace(); + } + return outByteStream.toByteArray(); + } + + public CChunk deserialize(CWorld world, DataInputStream dataStream) throws IOException { + int worldVersion = dataStream.readByte(); + byte[] blockData = readCompressedBytes(dataStream); + return deserializeChunk(world, blockData); + } + + private CChunk deserializeChunk(CWorld cWorld, byte[] bytes) throws IOException { + String world = cWorld.getWorldName(); + DataInputStream chunkData = new DataInputStream(new ByteArrayInputStream(bytes)); + // read coordinate + int x = chunkData.readInt(); + int z = chunkData.readInt(); + ChunkCoordinate coordinate = new ChunkCoordinate(x, z); + // read loading info + int loadedSeconds = chunkData.readInt(); + long lastLoadedTime = chunkData.readLong(); + // read task queue + int tasksSize = chunkData.readInt(); + PriorityQueue queue = new PriorityQueue<>(Math.max(11, tasksSize)); + for (int i = 0; i < tasksSize; i++) { + int time = chunkData.readInt(); + ChunkPos pos = new ChunkPos(chunkData.readInt()); + queue.add(new TickTask(time, pos)); + } + // read ticked blocks + int tickedSize = chunkData.readInt(); + HashSet tickedSet = new HashSet<>(Math.max(11, tickedSize)); + for (int i = 0; i < tickedSize; i++) { + tickedSet.add(new ChunkPos(chunkData.readInt())); + } + // read block data + ConcurrentHashMap sectionMap = new ConcurrentHashMap<>(); + int sections = chunkData.readInt(); + // read sections + for (int i = 0; i < sections; i++) { + ConcurrentHashMap blockMap = new ConcurrentHashMap<>(); + int sectionID = chunkData.readInt(); + byte[] sectionBytes = new byte[chunkData.readInt()]; + chunkData.read(sectionBytes); + DataInputStream sectionData = new DataInputStream(new ByteArrayInputStream(sectionBytes)); + int blockAmount = sectionData.readInt(); + // read blocks + for (int j = 0; j < blockAmount; j++){ + byte[] blockData = new byte[sectionData.readInt()]; + sectionData.read(blockData); + CompoundMap block = readCompound(blockData).getValue(); + String type = (String) block.get("type").getValue(); + CompoundMap data = (CompoundMap) block.get("data").getValue(); + switch (type) { + case "CROP" -> { + for (int pos : (int[]) block.get("pos").getValue()) { + ChunkPos chunkPos = new ChunkPos(pos); + blockMap.put(chunkPos, new MemoryCrop(chunkPos.getLocation(world, coordinate), new CompoundMap(data))); + } + } + case "POT" -> { + for (int pos : (int[]) block.get("pos").getValue()) { + ChunkPos chunkPos = new ChunkPos(pos); + blockMap.put(chunkPos, new MemoryPot(chunkPos.getLocation(world, coordinate), new CompoundMap(data))); + } + } + case "SPRINKLER" -> { + for (int pos : (int[]) block.get("pos").getValue()) { + ChunkPos chunkPos = new ChunkPos(pos); + blockMap.put(chunkPos, new MemorySprinkler(chunkPos.getLocation(world, coordinate), new CompoundMap(data))); + } + } + case "SCARECROW" -> { + for (int pos : (int[]) block.get("pos").getValue()) { + ChunkPos chunkPos = new ChunkPos(pos); + blockMap.put(chunkPos, new MemoryScarecrow(chunkPos.getLocation(world, coordinate), new CompoundMap(data))); + } + } + case "GLASS" -> { + for (int pos : (int[]) block.get("pos").getValue()) { + ChunkPos chunkPos = new ChunkPos(pos); + blockMap.put(chunkPos, new MemoryGlass(chunkPos.getLocation(world, coordinate), new CompoundMap(data))); + } + } + } + } + CSection cSection = new CSection(sectionID, blockMap); + sectionMap.put(sectionID, cSection); + } + + return new CChunk(cWorld, coordinate, loadedSeconds, lastLoadedTime, sectionMap, queue, tickedSet); + } + + private byte[] serializeChunk(SerializableChunk chunk) throws IOException { + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384); + DataOutputStream outStream = new DataOutputStream(outByteStream); + outStream.writeInt(chunk.getX()); + outStream.writeInt(chunk.getZ()); + outStream.writeInt(chunk.getLoadedSeconds()); + outStream.writeLong(chunk.getLastLoadedTime()); + // write queue + int[] queue = chunk.getQueuedTasks(); + outStream.writeInt(queue.length / 2); + for (int i : queue) { + outStream.writeInt(i); + } + // write ticked blocks + int[] tickedSet = chunk.getTicked(); + outStream.writeInt(tickedSet.length); + for (int i : tickedSet) { + outStream.writeInt(i); + } + // write block data + List sectionsToSave = chunk.getSections(); + outStream.writeInt(sectionsToSave.size()); + for (SerializableSection section : sectionsToSave) { + outStream.writeInt(section.getSectionID()); + byte[] blockData = serializeBlocks(section.getBlocks()); + outStream.writeInt(blockData.length); + outStream.write(blockData); + } + return outByteStream.toByteArray(); + } + + private byte[] serializeBlocks(Collection blocks) throws IOException { + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(16384); + DataOutputStream outStream = new DataOutputStream(outByteStream); + outStream.writeInt(blocks.size()); + for (CompoundTag block : blocks) { + byte[] blockData = serializeCompoundTag(block); + outStream.writeInt(blockData.length); + outStream.write(blockData); + } + return outByteStream.toByteArray(); + } + + private byte[] readCompressedBytes(DataInputStream dataStream) throws IOException { + int compressedLength = dataStream.readInt(); + int decompressedLength = dataStream.readInt(); + byte[] compressedData = new byte[compressedLength]; + byte[] decompressedData = new byte[decompressedLength]; + + dataStream.read(compressedData); + Zstd.decompress(decompressedData, compressedData); + return decompressedData; + } + + public SerializableChunk toSerializableChunk(CChunk chunk) { + ChunkCoordinate chunkCoordinate = chunk.getChunkCoordinate(); + return new SerializableChunk( + chunkCoordinate.x(), + chunkCoordinate.z(), + chunk.getLoadedSeconds(), + chunk.getLastLoadedTime(), + Arrays.stream(chunk.getSectionsForSerialization()).map(this::toSerializableSection).toList(), + queueToIntArray(chunk.getQueue()), + tickedBlocksToArray(chunk.getTickedBlocks()) + ); + } + + private int[] tickedBlocksToArray(Set set) { + int[] ticked = new int[set.size()]; + int i = 0; + for (ChunkPos pos : set) { + ticked[i] = pos.getPosition(); + i++; + } + return ticked; + } + + private int[] queueToIntArray(PriorityQueue queue) { + int size = queue.size() * 2; + int[] tasks = new int[size]; + int i = 0; + for (TickTask task : queue) { + tasks[i * 2] = task.getTime(); + tasks[i * 2 + 1] = task.getChunkPos().getPosition(); + i++; + } + return tasks; + } + + private SerializableSection toSerializableSection(CSection section) { + return new SerializableSection(section.getSectionID(), toCompoundTags(section.getBlockMap())); + } + + private List toCompoundTags(Map blocks) { + ArrayList tags = new ArrayList<>(blocks.size()); + Map> blockToPosMap = new HashMap<>(); + for (Map.Entry entry : blocks.entrySet()) { + ChunkPos coordinate = entry.getKey(); + CustomCropsBlock block = entry.getValue(); + List coordinates = blockToPosMap.computeIfAbsent(block, k -> new ArrayList<>()); + coordinates.add(coordinate.getPosition()); + } + for (Map.Entry> entry : blockToPosMap.entrySet()) { + tags.add(new CompoundTag("", toCompoundMap(entry.getKey(), entry.getValue()))); + } + return tags; + } + + private CompoundMap toCompoundMap(CustomCropsBlock block, List pos) { + CompoundMap map = new CompoundMap(); + int[] result = new int[pos.size()]; + for (int i = 0; i < pos.size(); i++) { + result[i] = pos.get(i); + } + map.put(new StringTag("type", block.getType().name())); + map.put(new IntArrayTag("pos", result)); + map.put(new CompoundTag("data", block.getCompoundMap().getOriginalMap())); + return map; + } + + private CompoundTag readCompound(byte[] bytes) throws IOException { + if (bytes.length == 0) + return null; + NBTInputStream nbtInputStream = new NBTInputStream( + new ByteArrayInputStream(bytes), + NBTInputStream.NO_COMPRESSION, + ByteOrder.BIG_ENDIAN + ); + return (CompoundTag) nbtInputStream.readTag(); + } + + private byte[] serializeCompoundTag(CompoundTag tag) throws IOException { + if (tag == null || tag.getValue().isEmpty()) + return new byte[0]; + ByteArrayOutputStream outByteStream = new ByteArrayOutputStream(); + NBTOutputStream outStream = new NBTOutputStream( + outByteStream, + NBTInputStream.NO_COMPRESSION, + ByteOrder.BIG_ENDIAN + ); + outStream.writeTag(tag); + return outByteStream.toByteArray(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java new file mode 100644 index 0000000..c1bf008 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/adaptor/SlimeWorldAdaptor.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world.adaptor; + +import com.infernalsuite.aswm.api.SlimePlugin; +import com.infernalsuite.aswm.api.events.LoadSlimeWorldEvent; +import net.momirealms.customcrops.api.manager.WorldManager; +import net.momirealms.customcrops.api.mechanic.world.ChunkCoordinate; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; + +public class SlimeWorldAdaptor extends AbstractWorldAdaptor { + + private final SlimePlugin slimePlugin; + + public SlimeWorldAdaptor(WorldManager worldManager) { + super(worldManager); + this.slimePlugin = (SlimePlugin) Bukkit.getPluginManager().getPlugin("SlimeWorldManager"); + } + + @EventHandler (ignoreCancelled = true) + public void onSlimeWorldLoad(LoadSlimeWorldEvent event) { + } + + @EventHandler(ignoreCancelled = true) + public void onWorldLoad(WorldLoadEvent event) { + + } + + @EventHandler (ignoreCancelled = true) + public void onWorldUnload(WorldUnloadEvent event) { + } + + @Override + public void unload(CustomCropsWorld customCropsWorld) { + + } + + @Override + public void init(CustomCropsWorld customCropsWorld) { + + } + + @Override + public void loadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate) { + + } + + @Override + public void unloadDynamicData(CustomCropsWorld customCropsWorld, ChunkCoordinate chunkCoordinate) { + + } + + @Override + public void saveDynamicData(CustomCropsWorld ccWorld, CustomCropsChunk chunk) { + + } + + private boolean isSlimeWorld(String name) { + return slimePlugin.getWorld(name) != null; + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java new file mode 100644 index 0000000..a12e221 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryCrop.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world.block; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.IntTag; +import com.flowpowered.nbt.StringTag; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.condition.Condition; +import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; +import net.momirealms.customcrops.api.mechanic.item.Crop; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.item.ItemType; +import net.momirealms.customcrops.api.mechanic.item.fertilizer.SpeedGrow; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.level.WorldCrop; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.util.LogUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.Objects; +import java.util.Optional; + +public class MemoryCrop extends AbstractCustomCropsBlock implements WorldCrop { + + public MemoryCrop(SimpleLocation location, String key, int point) { + super(location, new CompoundMap()); + setData("point", new IntTag("point", point)); + setData("key", new StringTag("key", key)); + setData("tick", new IntTag("tick", 0)); + } + + public MemoryCrop(SimpleLocation location, CompoundMap properties) { + super(location, properties); + } + + @Override + public String getKey() { + return getData("key").getAsStringTag() + .map(StringTag::getValue) + .orElse(""); + } + + @Override + public int getPoint() { + return getData("point").getAsIntTag().map(IntTag::getValue).orElse(0); + } + + @Override + public void setPoint(int point) { + setData("point", new IntTag("point", point)); + } + + @Override + public Crop getConfig() { + return CustomCropsPlugin.get().getItemManager().getCropByID(getKey()); + } + + @Override + public ItemType getType() { + return ItemType.CROP; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; + return Objects.equals(getCompoundMap(), that.getCompoundMap()); + } + + @Override + public int hashCode() { + return getKey().hashCode() + getPoint() * 17; + } + + @Override + public void tick(int interval) { + if (canTick(interval)) { + tick(); + } + } + + private void tick() { + Crop crop = getConfig(); + if (crop == null) { + LogUtils.warn("Found a crop without config at " + getLocation() + ". Try removing the data."); + CustomCropsPlugin.get().getWorldManager().removeCropAt(getLocation()); + return; + } + + SimpleLocation location = getLocation(); + Location bukkitLocation = location.getBukkitLocation(); + + int previous = getPoint(); + + // check death conditions + for (DeathConditions deathConditions : crop.getDeathConditions()) { + for (Condition condition : deathConditions.getConditions()) { + if (condition.isConditionMet(this)) { + CustomCropsPlugin.get().getScheduler().runTaskSyncLater(() -> { + CustomCropsPlugin.get().getWorldManager().removeCropAt(location); + CustomCropsPlugin.get().getItemManager().removeAnythingAt(bukkitLocation); + if (deathConditions.getDeathItem() != null) { + CustomCropsPlugin.get().getItemManager().placeItem(bukkitLocation, deathConditions.getItemCarrier(), deathConditions.getDeathItem()); + } + }, bukkitLocation, deathConditions.getDeathDelay()); + return; + } + } + } + + // don't check grow conditions if it's already ripe + if (previous >= crop.getMaxPoints()) { + return; + } + + // check grow conditions + for (Condition condition : crop.getGrowConditions().getConditions()) { + if (!condition.isConditionMet(this)) { + return; + } + } + + // check pot & fertilizer + Optional pot = CustomCropsPlugin.get().getWorldManager().getPotAt(location.copy().add(0,-1,0)); + if (pot.isEmpty()) { + return; + } + + int point = 1; + Fertilizer fertilizer = pot.get().getFertilizer(); + if (fertilizer instanceof SpeedGrow speedGrow) { + point += speedGrow.getPointBonus(); + } + + int x = Math.min(previous + point, crop.getMaxPoints()); + setPoint(x); + String pre = crop.getStageItemByPoint(previous); + String after = crop.getStageItemByPoint(x); + + CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { + for (int i = previous + 1; i <= x; i++) { + Crop.Stage stage = crop.getStageByPoint(i); + if (stage != null) { + stage.trigger(ActionTrigger.GROW, new State(null, new ItemStack(Material.AIR), bukkitLocation)); + } + } + if (pre.equals(after)) return; + CustomCropsPlugin.get().getItemManager().removeAnythingAt(bukkitLocation); + CustomCropsPlugin.get().getItemManager().placeItem(bukkitLocation, crop.getItemCarrier(), after); + }, bukkitLocation); + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java new file mode 100644 index 0000000..b00ffb4 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryGlass.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world.block; + +import com.flowpowered.nbt.CompoundMap; +import net.momirealms.customcrops.api.mechanic.item.ItemType; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.level.WorldGlass; + +import java.util.Objects; + +public class MemoryGlass extends AbstractCustomCropsBlock implements WorldGlass { + + public MemoryGlass(SimpleLocation location) { + super(location, new CompoundMap()); + } + + public MemoryGlass(SimpleLocation location, CompoundMap properties) { + super(location, properties); + } + + @Override + public ItemType getType() { + return ItemType.GREENHOUSE; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; + return Objects.equals(getCompoundMap(), that.getCompoundMap()); + } + + @Override + public int hashCode() { + return 1821739123; + } + + @Override + public void tick(int interval) { + + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java new file mode 100644 index 0000000..7d9a6c9 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryPot.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world.block; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.IntTag; +import com.flowpowered.nbt.StringTag; +import com.flowpowered.nbt.Tag; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.mechanic.item.Fertilizer; +import net.momirealms.customcrops.api.mechanic.item.ItemType; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsChunk; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.util.LogUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.data.Waterlogged; + +import java.util.Objects; + +public class MemoryPot extends AbstractCustomCropsBlock implements WorldPot { + + public MemoryPot(SimpleLocation location, CompoundMap compoundMap) { + super(location, compoundMap); + } + + public MemoryPot(SimpleLocation location, String key) { + super(location, new CompoundMap()); + setData("key", new StringTag("key", key)); + setData("water", new IntTag("water", 0)); + setData("fertilizer", new StringTag("fertilizer", "")); + setData("fertilizer-times", new IntTag("fertilizer-times", 0)); + } + + @Override + public String getKey() { + return getData("key").getAsStringTag() + .map(StringTag::getValue) + .orElse(""); + } + + @Override + public int getWater() { + return getData("water").getAsIntTag().map(IntTag::getValue).orElse(0); + } + + @Override + public void setWater(int water) { + setData("water", new IntTag("water", water)); + } + + @Override + public Fertilizer getFertilizer() { + Tag tag = getData("fertilizer"); + if (tag == null) return null; + return tag.getAsStringTag() + .map(strTag -> { + String key = strTag.getValue(); + return CustomCropsPlugin.get().getItemManager().getFertilizerByID(key); + }) + .orElse(null); + } + + @Override + public void setFertilizer(Fertilizer fertilizer) { + setData("fertilizer", new StringTag("fertilizer", fertilizer.getKey())); + setData("fertilizer-times", new IntTag("fertilizer-times", fertilizer.getTimes())); + } + + @Override + public void removeFertilizer() { + setData("fertilizer", new StringTag("fertilizer", "")); + setData("fertilizer-times", new IntTag("fertilizer-times", 0)); + } + + @Override + public int getFertilizerTimes() { + return getData("fertilizer-times").getAsIntTag().map(IntTag::getValue).orElse(0); + } + + @Override + public void setFertilizerTimes(int times) { + setData("fertilizer-times", new IntTag("fertilizer-times", times)); + } + + @Override + public Pot getConfig() { + return CustomCropsPlugin.get().getItemManager().getPotByID(getKey()); + } + + @Override + public void tickWater(CustomCropsChunk chunk) { + Pot pot = getConfig(); + if (pot == null) { + LogUtils.warn("Found a pot without config at " + getLocation() + ". Try removing the data."); + CustomCropsPlugin.get().getWorldManager().removePotAt(getLocation()); + return; + } + SimpleLocation location = getLocation(); + Location bukkitLocation = location.getBukkitLocation(); + World world = location.getBukkitWorld(); + if (world == null || bukkitLocation == null) return; + + if (pot.isRainDropAccepted()) { + if (world.hasStorm() || (!world.isClearWeather() && !world.isThundering())) { + double temperature = world.getTemperature(location.getX(), location.getY(), location.getZ()); + if (temperature > 0.15 && temperature < 0.85) { + Block highest = world.getHighestBlockAt(location.getX(), location.getZ()); + if (highest.getLocation().getY() == location.getY()) { + addWaterToPot(pot, location); + return; + } + } + } + } + if (pot.isNearbyWaterAccepted()) { + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + for (int k : new int[]{0, 1}) { + Block block = bukkitLocation.clone().add(i,k,j).getBlock(); + if (block.getType() == Material.WATER || (block.getBlockData() instanceof Waterlogged waterlogged && waterlogged.isWaterlogged())) { + addWaterToPot(pot, location); + return; + } + } + } + } + } + } + + private void addWaterToPot(Pot pot, SimpleLocation location) { + int previous = getWater(); + if (previous >= pot.getStorage()) return; + setWater(Math.min(previous + 1, pot.getStorage())); + if (previous == 0) { + CustomCropsPlugin.get().getScheduler().runTaskSync(() -> CustomCropsPlugin.get().getItemManager().updatePotState(location.getBukkitLocation(), pot, true, getFertilizer()), location.getBukkitLocation()); + } + } + + @Override + public ItemType getType() { + return ItemType.POT; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; + return Objects.equals(getCompoundMap(), that.getCompoundMap()); + } + + @Override + public int hashCode() { + return getKey().hashCode() + getWater() * 17; + } + + @Override + public void tick(int interval) { + if (canTick(interval)) { + tick(); + } + } + + private void tick() { + Pot pot = getConfig(); + if (pot == null) { + LogUtils.warn("Found a pot without config at " + getLocation() + ". Try removing the data."); + CustomCropsPlugin.get().getWorldManager().removePotAt(getLocation()); + return; + } + + boolean loseFertilizer = false; + boolean loseWater = false; + int times = getFertilizerTimes(); + if (times > 0) { + if (--times <= 0) { + removeFertilizer(); + loseFertilizer = true; + } else { + setFertilizerTimes(times); + } + } + + int water = getWater(); + if (water > 0) { + if (--water <= 0) { + loseWater = true; + } + setWater(water); + } + + if (loseFertilizer || loseWater) { + Location location = getLocation().getBukkitLocation(); + CustomCropsPlugin.get().getScheduler().runTaskSync(() -> + CustomCropsPlugin.get().getItemManager() + .updatePotState( + location, + pot, + getWater() > 0, + getFertilizer() + ), location + ); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java new file mode 100644 index 0000000..0c78c17 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemoryScarecrow.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world.block; + +import com.flowpowered.nbt.CompoundMap; +import net.momirealms.customcrops.api.mechanic.item.ItemType; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.level.WorldScarecrow; + +import java.util.Objects; + +public class MemoryScarecrow extends AbstractCustomCropsBlock implements WorldScarecrow { + + public MemoryScarecrow(SimpleLocation location) { + super(location, new CompoundMap()); + } + + public MemoryScarecrow(SimpleLocation location, CompoundMap properties) { + super(location, properties); + } + + @Override + public ItemType getType() { + return ItemType.SCARECROW; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; + return Objects.equals(getCompoundMap(), that.getCompoundMap()); + } + + @Override + public int hashCode() { + return 28371283; + } + + @Override + public void tick(int interval) { + + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java new file mode 100644 index 0000000..c275e29 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/mechanic/world/block/MemorySprinkler.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.mechanic.world.block; + +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.IntTag; +import com.flowpowered.nbt.StringTag; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.item.ItemType; +import net.momirealms.customcrops.api.mechanic.item.Pot; +import net.momirealms.customcrops.api.mechanic.item.Sprinkler; +import net.momirealms.customcrops.api.mechanic.requirement.State; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; +import net.momirealms.customcrops.api.mechanic.world.level.AbstractCustomCropsBlock; +import net.momirealms.customcrops.api.mechanic.world.level.CustomCropsWorld; +import net.momirealms.customcrops.api.mechanic.world.level.WorldPot; +import net.momirealms.customcrops.api.mechanic.world.level.WorldSprinkler; +import net.momirealms.customcrops.api.util.LogUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.Objects; +import java.util.Optional; + +public class MemorySprinkler extends AbstractCustomCropsBlock implements WorldSprinkler { + + public MemorySprinkler(SimpleLocation location, CompoundMap compoundMap) { + super(location, compoundMap); + } + + public MemorySprinkler(SimpleLocation location, String key, int water) { + super(location, new CompoundMap()); + setData("water", new IntTag("water", water)); + setData("key", new StringTag("key", key)); + } + + @Override + public int getWater() { + return getData("water").getAsIntTag().map(IntTag::getValue).orElse(0); + } + + @Override + public void setWater(int water) { + setData("water", new IntTag("water", water)); + } + + @Override + public String getKey() { + return getData("key").getAsStringTag() + .map(StringTag::getValue) + .orElse(""); + } + + @Override + public Sprinkler getConfig() { + return CustomCropsPlugin.get().getItemManager().getSprinklerByID(getKey()); + } + + @Override + public ItemType getType() { + return ItemType.SPRINKLER; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AbstractCustomCropsBlock that = (AbstractCustomCropsBlock) o; + return Objects.equals(getCompoundMap(), that.getCompoundMap()); + } + + @Override + public int hashCode() { + return getKey().hashCode() + getWater() * 17; + } + + @Override + public void tick(int interval) { + if (canTick(interval)) { + tick(); + } + } + + private void tick() { + Sprinkler sprinkler = getConfig(); + if (sprinkler == null) { + LogUtils.warn("Found a sprinkler without config at " + getLocation() + ". Try removing the data."); + CustomCropsPlugin.get().getWorldManager().removeSprinklerAt(getLocation()); + return; + } + + SimpleLocation location = getLocation(); + boolean updateState; + if (!sprinkler.isInfinite()) { + int water = getWater(); + if (water <= 0) { + return; + } + setWater(--water); + updateState = water == 0; + } else { + updateState = false; + } + + Location bukkitLocation = location.getBukkitLocation(); + if (bukkitLocation == null) return; + CustomCropsPlugin.get().getScheduler().runTaskSync(() -> { + sprinkler.trigger(ActionTrigger.WORK, new State(null, new ItemStack(Material.AIR), bukkitLocation)); + if (updateState && sprinkler.get3DItemWithWater() != null) { + CustomCropsPlugin.get().getItemManager().removeAnythingAt(bukkitLocation); + CustomCropsPlugin.get().getItemManager().placeItem(bukkitLocation, sprinkler.getItemCarrier(), sprinkler.get3DItemID()); + } + }, bukkitLocation); + + int range = sprinkler.getRange(); + CustomCropsWorld world = CustomCropsPlugin.get().getWorldManager().getCustomCropsWorld(location.getWorldName()).get(); + for (int i = -range; i <= range; i++) { + for (int j = -range; j <= range; j++) { + for (int k : new int[]{-1,0}) { + SimpleLocation potLocation = location.copy().add(i,k,j); + Optional pot = world.getPotAt(potLocation); + if (pot.isPresent()) { + WorldPot worldPot = pot.get(); + if (sprinkler.getPotWhitelist().contains(worldPot.getKey())) { + Pot potConfig = worldPot.getConfig(); + if (potConfig != null) { + int current = worldPot.getWater(); + if (current >= potConfig.getStorage()) { + continue; + } + worldPot.setWater(current + sprinkler.getWater()); + if (current == 0) { + CustomCropsPlugin.get().getScheduler().runTaskSync(() -> CustomCropsPlugin.get().getItemManager().updatePotState(potLocation.getBukkitLocation(), potConfig, true, worldPot.getFertilizer()), potLocation.getBukkitLocation()); + } + } + } + } + } + } + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/BukkitSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/BukkitSchedulerImpl.java new file mode 100644 index 0000000..ea59af1 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/scheduler/BukkitSchedulerImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.scheduler; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.scheduler.CancellableTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.scheduler.BukkitTask; + +public class BukkitSchedulerImpl implements SyncScheduler { + + private final CustomCropsPlugin plugin; + + public BukkitSchedulerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void runSyncTask(Runnable runnable, Location location) { + if (Bukkit.isPrimaryThread()) + runnable.run(); + else + Bukkit.getScheduler().runTask(plugin, runnable); + } + + @Override + public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) { + return new BukkitCancellableTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period)); + } + + @Override + public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { + if (delay == 0) { + if (Bukkit.isPrimaryThread()) runnable.run(); + else Bukkit.getScheduler().runTask(plugin, runnable); + return new BukkitCancellableTask(null); + } + return new BukkitCancellableTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay)); + } + + public static class BukkitCancellableTask implements CancellableTask { + + private final BukkitTask bukkitTask; + + public BukkitCancellableTask(BukkitTask bukkitTask) { + this.bukkitTask = bukkitTask; + } + + @Override + public void cancel() { + if (this.bukkitTask != null) + this.bukkitTask.cancel(); + } + + @Override + public boolean isCancelled() { + if (this.bukkitTask == null) return true; + return this.bukkitTask.isCancelled(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java new file mode 100644 index 0000000..931eb31 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/scheduler/FoliaSchedulerImpl.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.scheduler; + +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.scheduler.CancellableTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; + +public class FoliaSchedulerImpl implements SyncScheduler { + + private final CustomCropsPlugin plugin; + + public FoliaSchedulerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + } + + @Override + public void runSyncTask(Runnable runnable, Location location) { + if (location == null) { + Bukkit.getGlobalRegionScheduler().execute(plugin, runnable); + } else { + Bukkit.getRegionScheduler().execute(plugin, location, runnable); + } + } + + @Override + public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delay, long period) { + if (location == null) { + return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, (scheduledTask -> runnable.run()), delay, period)); + } + return new FoliaCancellableTask(Bukkit.getRegionScheduler().runAtFixedRate(plugin, location, (scheduledTask -> runnable.run()), delay, period)); + } + + @Override + public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay) { + if (delay == 0) { + if (location == null) { + return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().run(plugin, (scheduledTask -> runnable.run()))); + } + return new FoliaCancellableTask(Bukkit.getRegionScheduler().run(plugin, location, (scheduledTask -> runnable.run()))); + } + if (location == null) { + return new FoliaCancellableTask(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, (scheduledTask -> runnable.run()), delay)); + } + return new FoliaCancellableTask(Bukkit.getRegionScheduler().runDelayed(plugin, location, (scheduledTask -> runnable.run()), delay)); + } + + public static class FoliaCancellableTask implements CancellableTask { + + private final ScheduledTask scheduledTask; + + public FoliaCancellableTask(ScheduledTask scheduledTask) { + this.scheduledTask = scheduledTask; + } + + @Override + public void cancel() { + this.scheduledTask.cancel(); + } + + @Override + public boolean isCancelled() { + return this.scheduledTask.isCancelled(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/SchedulerImpl.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/SchedulerImpl.java new file mode 100644 index 0000000..b493db4 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/scheduler/SchedulerImpl.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.scheduler; + +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.manager.ConfigManager; +import net.momirealms.customcrops.api.scheduler.CancellableTask; +import net.momirealms.customcrops.api.scheduler.Scheduler; +import net.momirealms.customcrops.api.util.LogUtils; +import org.bukkit.Location; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * A scheduler implementation responsible for scheduling and managing tasks in a multi-threaded environment. + */ +public class SchedulerImpl implements Scheduler { + + private final SyncScheduler syncScheduler; + private final ScheduledThreadPoolExecutor schedule; + private final CustomCropsPlugin plugin; + + public SchedulerImpl(CustomCropsPlugin plugin) { + this.plugin = plugin; + this.syncScheduler = plugin.getVersionManager().hasRegionScheduler() ? + new FoliaSchedulerImpl(plugin) : new BukkitSchedulerImpl(plugin); + this.schedule = new ScheduledThreadPoolExecutor(1); + this.schedule.setMaximumPoolSize(1); + this.schedule.setKeepAliveTime(30, TimeUnit.SECONDS); + this.schedule.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); + } + + public void reload() { + try { + this.schedule.setMaximumPoolSize(ConfigManager.maximumPoolSize()); + this.schedule.setCorePoolSize(ConfigManager.corePoolSize()); + this.schedule.setKeepAliveTime(ConfigManager.keepAliveTime(), TimeUnit.SECONDS); + } catch (IllegalArgumentException e) { + LogUtils.warn("Failed to create thread pool. Please lower the corePoolSize in config.yml.", e); + } + } + + public void shutdown() { + if (this.schedule != null && !this.schedule.isShutdown()) + this.schedule.shutdown(); + } + + @Override + public void runTaskSync(Runnable runnable, Location location) { + this.syncScheduler.runSyncTask(runnable, location); + } + + @Override + public void runTaskAsync(Runnable runnable) { + try { + this.schedule.execute(runnable); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks) { + return this.syncScheduler.runTaskSyncTimer(runnable, location, delayTicks, periodTicks); + } + + @Override + public CancellableTask runTaskAsyncLater(Runnable runnable, long delay, TimeUnit timeUnit) { + return new ScheduledTask(schedule.schedule(() -> { + try { + runnable.run(); + } catch (Exception e) { + e.printStackTrace(); + } + }, delay, timeUnit)); + } + + @Override + public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delay, TimeUnit timeUnit) { + return new ScheduledTask(schedule.schedule(() -> runTaskSync(runnable, location), delay, timeUnit)); + } + + @Override + public CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks) { + return this.syncScheduler.runTaskSyncLater(runnable, location, delayTicks); + } + + @Override + public CancellableTask runTaskAsyncTimer(Runnable runnable, long delay, long period, TimeUnit timeUnit) { + return new ScheduledTask(schedule.scheduleAtFixedRate(() -> { + try { + runnable.run(); + } catch (Exception e) { + e.printStackTrace(); + } + }, delay, period, timeUnit)); + } + + public static class ScheduledTask implements CancellableTask { + + private final ScheduledFuture scheduledFuture; + + public ScheduledTask(ScheduledFuture scheduledFuture) { + this.scheduledFuture = scheduledFuture; + } + + @Override + public void cancel() { + this.scheduledFuture.cancel(false); + } + + @Override + public boolean isCancelled() { + return this.scheduledFuture.isCancelled(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/SchedulerPlatform.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/SyncScheduler.java similarity index 63% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/SchedulerPlatform.java rename to plugin/src/main/java/net/momirealms/customcrops/scheduler/SyncScheduler.java index 3a53f2b..b8c3a65 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/scheduler/SchedulerPlatform.java +++ b/plugin/src/main/java/net/momirealms/customcrops/scheduler/SyncScheduler.java @@ -15,19 +15,16 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.scheduler; +package net.momirealms.customcrops.scheduler; +import net.momirealms.customcrops.api.scheduler.CancellableTask; import org.bukkit.Location; -import org.jetbrains.annotations.NotNull; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; +public interface SyncScheduler { -public interface SchedulerPlatform { + void runSyncTask(Runnable runnable, Location location); - Future callSyncMethod(@NotNull Callable task); + CancellableTask runTaskSyncTimer(Runnable runnable, Location location, long delayTicks, long periodTicks); - void runTask(Runnable runnable); - - void runTask(Runnable runnable, Location location); + CancellableTask runTaskSyncLater(Runnable runnable, Location location, long delayTicks); } diff --git a/plugin/src/main/java/net/momirealms/customcrops/api/object/season/CCSeason.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/ReplaceTask.java similarity index 50% rename from plugin/src/main/java/net/momirealms/customcrops/api/object/season/CCSeason.java rename to plugin/src/main/java/net/momirealms/customcrops/scheduler/task/ReplaceTask.java index b323561..163fa77 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/api/object/season/CCSeason.java +++ b/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/ReplaceTask.java @@ -15,30 +15,32 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.api.object.season; +package net.momirealms.customcrops.scheduler.task; -import net.momirealms.customcrops.api.object.CCWorldSeason; -import net.momirealms.customcrops.api.object.basic.MessageManager; +import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; +import net.momirealms.customcrops.api.mechanic.world.SimpleLocation; -public enum CCSeason implements CCWorldSeason { +public class ReplaceTask { - SPRING(MessageManager.spring), - SUMMER(MessageManager.summer), - AUTUMN(MessageManager.autumn), - WINTER(MessageManager.winter), - UNKNOWN(MessageManager.noSeason); + private final SimpleLocation simpleLocation; + private final ItemCarrier carrier; + private final String id; - private final String display; - - CCSeason(String display) { - this.display = display; + public ReplaceTask(SimpleLocation simpleLocation, ItemCarrier carrier, String id) { + this.simpleLocation = simpleLocation; + this.carrier = carrier; + this.id = id; } - public String getDisplay() { - return display; + public SimpleLocation getSimpleLocation() { + return simpleLocation; } - public String getSeason() { - return this.name(); + public ItemCarrier getCarrier() { + return carrier; + } + + public String getID() { + return id; } } diff --git a/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/TickTask.java b/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/TickTask.java new file mode 100644 index 0000000..f319d36 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/scheduler/task/TickTask.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.scheduler.task; + +import net.momirealms.customcrops.api.mechanic.world.ChunkPos; +import org.jetbrains.annotations.NotNull; + +public class TickTask implements Comparable { + + private static int taskID; + private final int time; + private final ChunkPos chunkPos; + private final int id; + + public TickTask(int time, ChunkPos chunkPos) { + this.time = time; + this.chunkPos = chunkPos; + this.id = taskID++; + } + + public ChunkPos getChunkPos() { + return chunkPos; + } + + public int getTime() { + return time; + } + + @Override + public int compareTo(@NotNull TickTask o) { + if (this.time > o.time) { + return 1; + } else if (this.time < o.time) { + return -1; + } else { + return Integer.compare(this.id, o.id); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/AdventureUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/AdventureUtils.java deleted file mode 100644 index 500eb8a..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/util/AdventureUtils.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.util; - -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.title.Title; -import net.momirealms.customcrops.CustomCrops; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.time.Duration; - -public class AdventureUtils { - - /** - * Get component from text - * @param text text - * @return component - */ - public static Component getComponentFromMiniMessage(String text) { - return MiniMessage.miniMessage().deserialize(replaceLegacy(text)); - } - - /** - * Send a message to a command sender - * @param sender sender - * @param s message - */ - public static void sendMessage(CommandSender sender, String s) { - if (s == null) return; - if (sender instanceof Player player) playerMessage(player, s); - else consoleMessage(s); - } - - /** - * Send a message to console - * @param s message - */ - public static void consoleMessage(String s) { - if (s == null) return; - Audience au = CustomCrops.getAdventure().sender(Bukkit.getConsoleSender()); - au.sendMessage(getComponentFromMiniMessage(s)); - } - - /** - * Send a message to a player - * @param player player - * @param s message - */ - public static void playerMessage(Player player, String s) { - if (s == null) return; - Audience au = CustomCrops.getAdventure().player(player); - au.sendMessage(getComponentFromMiniMessage(s)); - } - - /** - * Send a title to a player - * @param player player - * @param s1 title - * @param s2 subtitle - * @param in in (ms) - * @param duration duration (ms) - * @param out out (ms) - */ - public static void playerTitle(Player player, String s1, String s2, int in, int duration, int out) { - Audience au = CustomCrops.getAdventure().player(player); - Title.Times times = Title.Times.times(Duration.ofMillis(in), Duration.ofMillis(duration), Duration.ofMillis(out)); - Title title = Title.title(getComponentFromMiniMessage(s1), getComponentFromMiniMessage(s2), times); - au.showTitle(title); - } - - /** - * Send a title to a player - * @param player player - * @param s1 title - * @param s2 subtitle - * @param in in (ms) - * @param duration duration (ms) - * @param out out (ms) - */ - public static void playerTitle(Player player, Component s1, Component s2, int in, int duration, int out) { - Audience au = CustomCrops.getAdventure().player(player); - Title.Times times = Title.Times.times(Duration.ofMillis(in), Duration.ofMillis(duration), Duration.ofMillis(out)); - Title title = Title.title(s1, s2, times); - au.showTitle(title); - } - - /** - * Send an actionbar to a player - * @param player player - * @param s actionbar - */ - public static void playerActionbar(Player player, String s) { - Audience au = CustomCrops.getAdventure().player(player); - au.sendActionBar(getComponentFromMiniMessage(s)); - } - - /** - * Play a sound to a player - * @param player player - * @param source sound source - * @param key sound key - * @param volume volume - * @param pitch pitch - */ - public static void playerSound(Player player, Sound.Source source, Key key, float volume, float pitch) { - Sound sound = Sound.sound(key, source, volume, pitch); - Audience au = CustomCrops.getAdventure().player(player); - au.playSound(sound); - } - - public static void playerSound(Player player, Sound sound) { - Audience au = CustomCrops.getAdventure().player(player); - au.playSound(sound); - } - - public static String replaceLegacy(String legacy) { - StringBuilder stringBuilder = new StringBuilder(); - char[] chars = legacy.toCharArray(); - for (int i = 0; i < chars.length; i++) { - if (isColorCode(chars[i])) { - if (i + 1 < chars.length) { - switch (chars[i+1]) { - case '0' -> stringBuilder.append(""); - case '1' -> stringBuilder.append(""); - case '2' -> stringBuilder.append(""); - case '3' -> stringBuilder.append(""); - case '4' -> stringBuilder.append(""); - case '5' -> stringBuilder.append(""); - case '6' -> stringBuilder.append(""); - case '7' -> stringBuilder.append(""); - case '8' -> stringBuilder.append(""); - case '9' -> stringBuilder.append(""); - case 'a' -> stringBuilder.append(""); - case 'b' -> stringBuilder.append(""); - case 'c' -> stringBuilder.append(""); - case 'd' -> stringBuilder.append(""); - case 'e' -> stringBuilder.append(""); - case 'f' -> stringBuilder.append(""); - case 'r' -> stringBuilder.append(""); - case 'l' -> stringBuilder.append(""); - case 'm' -> stringBuilder.append(""); - case 'o' -> stringBuilder.append(""); - case 'n' -> stringBuilder.append(""); - case 'k' -> stringBuilder.append(""); - case 'x' -> { - if (i + 13 >= chars.length - || !isColorCode(chars[i+2]) - || !isColorCode(chars[i+4]) - || !isColorCode(chars[i+6]) - || !isColorCode(chars[i+8]) - || !isColorCode(chars[i+10]) - || !isColorCode(chars[i+12])) { - stringBuilder.append(chars[i]); - continue; - } - stringBuilder - .append("<#") - .append(chars[i+3]) - .append(chars[i+5]) - .append(chars[i+7]) - .append(chars[i+9]) - .append(chars[i+11]) - .append(chars[i+13]) - .append(">"); - i += 12; - } - default -> { - stringBuilder.append(chars[i]); - continue; - } - } - i++; - } else { - stringBuilder.append(chars[i]); - } - } - else { - stringBuilder.append(chars[i]); - } - } - return stringBuilder.toString(); - } - - private static boolean isColorCode(char c) { - return c == '§' || c == '&'; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java deleted file mode 100644 index 9caf086..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ConfigUtils.java +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright (C) <2022> - * - * 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 - * 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 . - */ - -package net.momirealms.customcrops.util; - -import dev.dejvokep.boostedyaml.YamlDocument; -import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; -import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; -import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; -import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; -import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.BoneMeal; -import net.momirealms.customcrops.api.object.InteractCrop; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.Pair; -import net.momirealms.customcrops.api.object.action.*; -import net.momirealms.customcrops.api.object.basic.ConfigManager; -import net.momirealms.customcrops.api.object.condition.Random; -import net.momirealms.customcrops.api.object.condition.*; -import net.momirealms.customcrops.api.object.crop.VariationCrop; -import net.momirealms.customcrops.api.object.fill.PassiveFillMethod; -import net.momirealms.customcrops.api.object.fill.PositiveFillMethod; -import net.momirealms.customcrops.api.object.loot.Loot; -import net.momirealms.customcrops.api.object.loot.OtherLoot; -import net.momirealms.customcrops.api.object.loot.QualityLoot; -import net.momirealms.customcrops.api.object.requirement.*; -import net.momirealms.customcrops.api.object.season.CCSeason; -import net.momirealms.customcrops.customplugin.Platform; -import net.momirealms.customcrops.helper.Log; -import org.bukkit.Particle; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.MemorySection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.intellij.lang.annotations.Subst; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.stream.Collectors; - -public class ConfigUtils { - - /** - * Get a config by name - * @param config_path config's path - * @return yaml - */ - public static YamlConfiguration getConfig(String config_path) { - File file = new File(CustomCrops.getInstance().getDataFolder(), config_path); - if (!file.exists()) { - CustomCrops.getInstance().saveResource(config_path, false); - if (CustomCrops.getInstance().getPlatform() == Platform.Oraxen) { - File generated = new File(CustomCrops.getInstance().getDataFolder(), config_path); - if (generated.exists() && generated.getName().endsWith(".yml")) { - removeNamespace(generated); - } - } - } - return YamlConfiguration.loadConfiguration(file); - } - - public static void removeNamespace(File file) { - String line; - StringBuilder sb = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { - while ((line = reader.readLine()) != null) { - sb.append(line).append(System.lineSeparator()); - } - } catch (IOException e) { - e.printStackTrace(); - } - try (BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { - writer.write(sb.toString().replace(" customcrops:", " ").replace("CHORUS", "TRIPWIRE").replace("", "")); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Update config - * @param fileName config - */ - public static void update(String fileName){ - try { - YamlDocument.create(new File(CustomCrops.getInstance().getDataFolder(), fileName), Objects.requireNonNull(CustomCrops.getInstance().getResource(fileName)), GeneralSettings.DEFAULT, LoaderSettings.builder().setAutoUpdate(true).build(), DumperSettings.DEFAULT, UpdaterSettings.builder().setVersioning(new BasicVersioning("config-version")).build()); - } catch (IOException e){ - Log.warn(e.getMessage()); - } - } - - /** - * Create a data file if not exists - * @param file file path - * @return yaml data - */ - @SuppressWarnings("ResultOfMethodCallIgnored") - public static YamlConfiguration readData(File file) { - if (!file.exists()) { - try { - file.getParentFile().mkdirs(); - file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - AdventureUtils.consoleMessage("[CustomFishing] Failed to generate data files!"); - } - } - return YamlConfiguration.loadConfiguration(file); - } - - @Nullable - public static DeathCondition[] getDeathConditions(ConfigurationSection section) { - if (section != null) { - List deathConditions = new ArrayList<>(); - for (String key : section.getKeys(false)) { - String model = section.getString(key + ".model"); - ConfigurationSection conditionSec = section.getConfigurationSection(key + ".conditions"); - if (conditionSec == null) { - AdventureUtils.consoleMessage("[CustomCrops] No condition is set for: " + section.getCurrentPath()); - continue; - } - List conditions = getConditions(conditionSec.getValues(false)); - deathConditions.add(new DeathCondition(model, conditions.toArray(new Condition[0]))); - if (model != null) CustomCrops.getInstance().getCropManager().registerDeadCrops(model); - } - return deathConditions.toArray(new DeathCondition[0]); - } - return null; - } - - @Nullable - public static Condition[] getConditions(ConfigurationSection section) { - if (section != null) { - return getConditions(section.getValues(false)).toArray(new Condition[0]); - } - return null; - } - - @NotNull - public static List getConditions(Map map) { - List conditions = new ArrayList<>(); - map.forEach((key, value) -> { - if (key.startsWith("&&")) { - if (map.get(key) instanceof MemorySection map2) { - conditions.add(new AndCondition(getConditions(map2.getValues(false)))); - } - } else if (key.startsWith("||")) { - if (map.get(key) instanceof MemorySection map2) { - conditions.add(new OrCondition(getConditions(map2.getValues(false)))); - } - } else { - if (map.get(key) instanceof MemorySection map2) { - String type = map2.getString("type"); - if (type == null) return; - switch (type) { - case "water_less_than" -> conditions.add(new WaterLessThan(map2.getInt("value"))); - case "water_more_than" -> conditions.add(new WaterMoreThan(map2.getInt("value"))); - case "unsuitable_season" -> { - if (!ConfigManager.enableSeason) return; - conditions.add(new WrongSeason(map2.getStringList("value").stream().map(s -> CCSeason.valueOf(s.toUpperCase(Locale.ENGLISH))).toList().toArray(new CCSeason[0]))); - } - case "suitable_season" -> { - if (!ConfigManager.enableSeason) return; - conditions.add(new RightSeason(map2.getStringList("value").stream().map(s -> CCSeason.valueOf(s.toUpperCase(Locale.ENGLISH))).toList().toArray(new CCSeason[0]))); - } - case "crow_attack" -> conditions.add(new CrowAttack(map2.getDouble("value.chance"), map2.getString("value.fly-model"), map2.getString("value.stand-model"))); - case "random" -> conditions.add(new Random(map2.getDouble("value"))); - case "weather" -> conditions.add(new Weather(map2.getStringList("value").toArray(new String[0]))); - } - } - } - }); - return conditions; - } - - @Nullable - public static Requirement[] getRequirementsWithMsg(ConfigurationSection section) { - if (section != null) { - List requirements = new ArrayList<>(); - for (String id : section.getKeys(false)) { - ConfigurationSection innerSec = section.getConfigurationSection(id); - if (innerSec == null) continue; - String type = innerSec.getString("type"); - if (type == null) continue; - String[] msg = innerSec.getStringList("message").size() == 0 ? (innerSec.getString("message") == null ? null : new String[]{innerSec.getString("message")}) : innerSec.getStringList("message").toArray(new String[0]); - ConfigurationSection actionSec = innerSec.getConfigurationSection("actions"); - switch (type) { - case "biome" -> requirements.add(new BiomeImpl(msg, getActions(actionSec), new HashSet<>(innerSec.getStringList("value")))); - case "!biome" -> requirements.add(new BlackBiomeImpl(msg, getActions(actionSec), new HashSet<>(innerSec.getStringList("value")))); - case "weather" -> requirements.add(new WeatherImpl(msg, getActions(actionSec), innerSec.getStringList("value").toArray(new String[0]))); - case "ypos" -> requirements.add(new YPosImpl(msg, getActions(actionSec), innerSec.getStringList("value"))); - case "season" -> { - if (!ConfigManager.enableSeason) continue; - requirements.add(new SeasonImpl(msg, getActions(actionSec), innerSec.getStringList("value").stream().map(str -> CCSeason.valueOf(str.toUpperCase(Locale.ENGLISH))).collect(Collectors.toList()))); - } - case "world" -> requirements.add(new WorldImpl(msg, getActions(actionSec), innerSec.getStringList("value"))); - case "permission" -> requirements.add(new PermissionImpl(msg, getActions(actionSec), innerSec.getString("value"))); - case "time" -> requirements.add(new TimeImpl(msg, getActions(actionSec), innerSec.getStringList("value"))); - case "skill-level" -> requirements.add(new SkillLevelImpl(msg, getActions(actionSec), innerSec.getInt("value"))); - case "job-level" -> requirements.add(new JobLevelImpl(msg, getActions(actionSec), innerSec.getInt("value.level"), innerSec.getString("value.job"))); - case "light" -> requirements.add(new LightLevelImpl(msg, getActions(actionSec), innerSec.getInt("value"))); - case "natural-light" -> requirements.add(new NaturalLightLevelImpl(msg, getActions(actionSec), innerSec.getInt("value"))); - case "date" -> requirements.add(new DateImpl(msg, getActions(actionSec), new HashSet<>(innerSec.getStringList("value")))); - case "max-entity-amount-in-chunk" -> requirements.add(new EntityAmountInChunkImpl(msg, getActions(actionSec), innerSec.getInt("value"))); - case "papi-condition" -> requirements.add(new CustomPapi(msg, getActions(actionSec), Objects.requireNonNull(innerSec.getConfigurationSection("value")).getValues(false))); - } - } - return requirements.toArray(new Requirement[0]); - } - return null; - } - - @Nullable - public static Action[] getActions(ConfigurationSection section) { - return getActions(section, null); - } - - @Nullable - public static Action[] getActions(ConfigurationSection section, String model_id) { - if (section != null) { - List actions = new ArrayList<>(); - for (String action_key : section.getKeys(false)) { - if (action_key.equals("requirements")) continue; - ConfigurationSection actionSec = section.getConfigurationSection(action_key); - if (actionSec == null) continue; - String type = actionSec.getString("type"); - if (type == null) continue; - switch (type) { - case "message" -> actions.add(new MessageActionImpl( - actionSec.getStringList("value").toArray(new String[0]), - actionSec.getDouble("chance", 1)) - ); - case "actionbar" -> actions.add(new ActionBarImpl( - actionSec.getString("value"), - actionSec.getDouble("chance", 1)) - ); - case "command" -> actions.add(new CommandActionImpl( - actionSec.getStringList("value").toArray(new String[0]), - actionSec.getDouble("chance", 1)) - ); - case "exp" -> actions.add(new VanillaXPImpl( - actionSec.getInt("value"), - false, - actionSec.getDouble("chance", 1)) - ); - case "mending" -> actions.add(new VanillaXPImpl( - actionSec.getInt("value"), - true, - actionSec.getDouble("chance", 1)) - ); - case "skill-xp" -> actions.add(new SkillXPImpl( - actionSec.getDouble("value"), - actionSec.getDouble("chance", 1)) - ); - case "job-xp" -> actions.add(new JobXPImpl( - actionSec.getDouble("value.xp"), - actionSec.getDouble("chance", 1), - actionSec.getString("value.job")) - ); - case "sound" -> actions.add(new SoundActionImpl( - actionSec.getString("value.source"), - actionSec.getString("value.key"), - (float) actionSec.getDouble("value.volume"), - (float) actionSec.getDouble("value.pitch")) - ); - case "particle" -> actions.add(new ParticleImpl( - Particle.valueOf(actionSec.getString("value.particle", "FLAME").toUpperCase(Locale.ENGLISH)), - actionSec.getInt("value.amount"), - actionSec.getDouble("value.offset")) - ); - case "potion-effect" -> { - PotionEffectType potionEffectType = PotionEffectType.getByName(actionSec.getString("value.type", "BLINDNESS").toUpperCase(Locale.ENGLISH)); - PotionEffect potionEffect = new PotionEffect( - potionEffectType == null ? PotionEffectType.LUCK : potionEffectType, - actionSec.getInt("value.duration"), - actionSec.getInt("value.amplifier") - ); - actions.add(new PotionEffectImpl(potionEffect, actionSec.getDouble("chance", 1))); - } - case "drop-items" -> { - ConfigurationSection lootSec = actionSec.getConfigurationSection("value"); - if (lootSec == null) continue; - boolean directToInv = lootSec.getBoolean("to-inventory", false); - ArrayList loots = new ArrayList<>(); - if (lootSec.contains("quality-crops")) { - String[] qualityLoots = new String[ConfigManager.defaultRatio.length]; - for (int i = 0; i < ConfigManager.defaultRatio.length; i++) { - qualityLoots[i] = lootSec.getString("quality-crops.items." + (i+1)); - if (qualityLoots[i] == null) { - AdventureUtils.consoleMessage("[CustomCrops] Error found at: " + model_id + " quality-crops.items." + (i+1) + ", which can't be null"); - } - } - loots.add(new QualityLoot( - lootSec.getInt("quality-crops.min"), - lootSec.getInt("quality-crops.max"), - qualityLoots - )); - } - if (lootSec.contains("other-items")) { - ConfigurationSection otherLootSec = lootSec.getConfigurationSection("other-items"); - if (otherLootSec == null) continue; - for (String inner_key : otherLootSec.getKeys(false)) { - OtherLoot otherLoot = new OtherLoot( - otherLootSec.getInt(inner_key + ".min"), - otherLootSec.getInt(inner_key + ".max"), - otherLootSec.getString(inner_key + ".item"), - otherLootSec.getDouble(inner_key + ".chance") - ); - loots.add(otherLoot); - } - } - actions.add(new DropItemImpl(loots.toArray(new Loot[0]), directToInv)); - } - case "break" -> actions.add(new BreakImpl( - actionSec.getBoolean("value", true), - model_id) - ); - case "replant" -> actions.add(new ReplantImpl( - actionSec.getInt("value.point"), - actionSec.getString("value.model"), - actionSec.getString("value.crop") - )); - case "variation" -> { - ConfigurationSection variationSec = actionSec.getConfigurationSection("value"); - if (variationSec == null) continue; - ArrayList variationCrops = new ArrayList<>(); - for (String inner_key : variationSec.getKeys(false)) { - VariationCrop variationCrop = new VariationCrop( - variationSec.getString(inner_key + ".item"), - ItemMode.valueOf(variationSec.getString(inner_key + ".type", "TripWire").toUpperCase(Locale.ENGLISH)), - variationSec.getDouble(inner_key + ".chance") - ); - variationCrops.add(variationCrop); - } - actions.add(new VariationImpl(variationCrops.toArray(new VariationCrop[0]))); - } - case "chain" -> actions.add(new ChainImpl( - getActions(actionSec.getConfigurationSection("value"), model_id), - getRequirementsWithMsg(actionSec.getConfigurationSection("requirements")), - actionSec.getDouble("chance") - )); - case "swing-hand" -> actions.add(new SwingHandImpl()); - case "give-money" -> actions.add(new GiveMoneyImpl( - actionSec.getDouble("value"), - actionSec.getDouble("chance", 1) - )); - } - } - return actions.toArray(new Action[0]); - } - return null; - } - - @Nullable - public static PassiveFillMethod[] getPassiveFillMethods(ConfigurationSection section) { - if (section == null) return null; - ArrayList passiveFillMethods = new ArrayList<>(); - for (String key : section.getKeys(false)) { - ConfigurationSection methodSec = section.getConfigurationSection(key); - if (methodSec == null) continue; - @Subst("namespace:key") String soundKey = methodSec.getString("sound", "minecraft:block.water.ambient"); - Sound sound = methodSec.contains("sound") ? Sound.sound(Key.key(soundKey), Sound.Source.PLAYER, 1, 1) : null; - PassiveFillMethod passiveFillMethod = new PassiveFillMethod( - methodSec.getString("item"), - methodSec.getString("return"), - methodSec.getInt("amount", 1), - methodSec.contains("particle") ? Particle.valueOf(methodSec.getString("particle", "WATER_SPLASH").toUpperCase(Locale.ENGLISH)) : null, - sound - ); - passiveFillMethods.add(passiveFillMethod); - } - return passiveFillMethods.toArray(new PassiveFillMethod[0]); - } - - public static double[] getQualityRatio(String str) { - String[] split = str.split("/"); - double[] ratio = new double[split.length]; - double weightTotal = Arrays.stream(split).mapToInt(Integer::parseInt).sum(); - double temp = 0; - for (int i = 0; i < ratio.length; i++) { - temp += Integer.parseInt(split[i]); - ratio[i] = temp / weightTotal; - } - return ratio; - } - - public static boolean isVanillaItem(String item) { - char[] chars = item.toCharArray(); - for (char character : chars) { - if ((character < 65 || character > 90) && character != 95) { - return false; - } - } - return true; - } - - @Nullable - public static BoneMeal[] getBoneMeals(ConfigurationSection section) { - if (section == null) return null; - ArrayList boneMeals = new ArrayList<>(); - for (String key : section.getKeys(false)) { - ConfigurationSection boneMealSec = section.getConfigurationSection(key); - if (boneMealSec == null) continue; - ConfigurationSection chanceSec = boneMealSec.getConfigurationSection("chance"); - if (chanceSec == null) { - AdventureUtils.consoleMessage("chance is not properly set for custom bone meal at: " + boneMealSec.getCurrentPath()); - continue; - } - ArrayList> pairs = new ArrayList<>(); - for (String point : chanceSec.getKeys(false)) { - Pair pair = Pair.of(chanceSec.getDouble(point), Integer.parseInt(point)); - pairs.add(pair); - } - @Subst("namespace:key") String soundKey = boneMealSec.getString("sound", "minecraft:item.bone_meal.use"); - Sound sound = boneMealSec.contains("sound") ? Sound.sound(Key.key(soundKey), Sound.Source.PLAYER, 1, 1) : null; - BoneMeal boneMeal = new BoneMeal( - boneMealSec.getString("item"), - boneMealSec.getString("return"), - pairs, - sound, - boneMealSec.contains("particle") ? Particle.valueOf(boneMealSec.getString("particle", "WATER_SPLASH").toUpperCase(Locale.ENGLISH)) : null - ); - boneMeals.add(boneMeal); - } - return boneMeals.toArray(new BoneMeal[0]); - } - - @Nullable - public static PositiveFillMethod[] getPositiveFillMethods(ConfigurationSection section) { - if (section == null) return null; - ArrayList methods = new ArrayList<>(); - for (String key : section.getKeys(false)) { - ConfigurationSection methodSec = section.getConfigurationSection(key); - if (methodSec == null) continue; - String id = methodSec.getString("target", "WATER"); - @Subst("namespace:key") String soundKey = methodSec.getString("sound", "minecraft:item.bucket.fill"); - Sound sound = Sound.sound(Key.key(soundKey), Sound.Source.PLAYER, 1, 1); - PositiveFillMethod method = new PositiveFillMethod( - id, - methodSec.getInt("amount"), - methodSec.contains("particle") ? Particle.valueOf(methodSec.getString("particle", "WATER_SPLASH").toUpperCase(Locale.ENGLISH)) : null, - sound - ); - methods.add(method); - } - return methods.toArray(new PositiveFillMethod[0]); - } - - public static InteractCrop[] getInteractActions(ConfigurationSection section, String stageModel) { - if (section == null) return null; - ArrayList interactCrops = new ArrayList<>(); - for (String key : section.getKeys(false)) { - ConfigurationSection innerSec = section.getConfigurationSection(key); - if (innerSec == null) continue; - InteractCrop interactCrop = new InteractCrop( - innerSec.getString("item"), - innerSec.getBoolean("reduce-amount", false), - innerSec.getString("return"), - getActions(innerSec.getConfigurationSection("actions"), stageModel), - getRequirementsWithMsg(innerSec.getConfigurationSection("requirements")) - ); - interactCrops.add(interactCrop); - } - return interactCrops.toArray(new InteractCrop[0]); - } - - public static int rgbToDecimal(String rgba) { - String[] split = rgba.split(","); - int r = Integer.parseInt(split[0]); - int g = Integer.parseInt(split[1]); - int b = Integer.parseInt(split[2]); - int a = Integer.parseInt(split[3]); - return (a << 24) | (r << 16) | (g << 8) | b; - } - - public static File getFile(String world, String fileName) { - File file; - if (ConfigManager.worldFolderPath.equals("")) { - file = new File(CustomCrops.getInstance().getDataFolder().getParentFile().getParentFile(), world + File.separator + "customcrops" + File.separator + fileName); - } else { - file = new File(ConfigManager.worldFolderPath, world + File.separator + "customcrops" + File.separator + fileName); - } - return file; - } - - public static File getFile(World world, String fileName) { - File file; - if (ConfigManager.worldFolderPath.equals("")) { - file = new File(world.getWorldFolder(), "customcrops" + File.separator + fileName); - } else { - file = new File(ConfigManager.worldFolderPath, world.getName() + File.separator + "customcrops" + File.separator + fileName); - } - return file; - } -} \ No newline at end of file diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java b/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java deleted file mode 100644 index 5e28694..0000000 --- a/plugin/src/main/java/net/momirealms/customcrops/util/ItemUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -package net.momirealms.customcrops.util; - -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -public class ItemUtils { - - public static int putLootsToBag(Inventory inventory, ItemStack itemStack, int amount) { - ItemMeta meta = itemStack.getItemMeta(); - int maxStackSize = itemStack.getMaxStackSize(); - for (ItemStack other : inventory.getStorageContents()) { - if (other != null) { - if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { - if (other.getAmount() < maxStackSize) { - int delta = maxStackSize - other.getAmount(); - if (amount > delta) { - other.setAmount(maxStackSize); - amount -= delta; - } else { - other.setAmount(amount + other.getAmount()); - return 0; - } - } - } - } - } - - if (amount > 0) { - for (ItemStack other : inventory.getStorageContents()) { - if (other == null) { - if (amount > maxStackSize) { - amount -= maxStackSize; - ItemStack cloned = itemStack.clone(); - cloned.setAmount(maxStackSize); - inventory.addItem(cloned); - } else { - ItemStack cloned = itemStack.clone(); - cloned.setAmount(amount); - inventory.addItem(cloned); - return 0; - } - } - } - } - - return amount; - } -} diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/ClassUtils.java b/plugin/src/main/java/net/momirealms/customcrops/utils/ClassUtils.java new file mode 100644 index 0000000..6cd18ac --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/utils/ClassUtils.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.utils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +public class ClassUtils { + + private ClassUtils() {} + + /** + * Attempts to find a class within a JAR file that extends or implements a given class or interface. + * + * @param file The JAR file in which to search for the class. + * @param clazz The base class or interface to match against. + * @param The type of the base class or interface. + * @return A Class object representing the found class, or null if not found. + * @throws IOException If there is an issue reading the JAR file. + * @throws ClassNotFoundException If the specified class cannot be found. + */ + @Nullable + public static Class findClass( + @NotNull File file, + @NotNull Class clazz + ) throws IOException, ClassNotFoundException { + if (!file.exists()) { + return null; + } + + URL jar = file.toURI().toURL(); + URLClassLoader loader = new URLClassLoader(new URL[]{jar}, clazz.getClassLoader()); + List matches = new ArrayList<>(); + List> classes = new ArrayList<>(); + + try (JarInputStream stream = new JarInputStream(jar.openStream())) { + JarEntry entry; + while ((entry = stream.getNextJarEntry()) != null) { + final String name = entry.getName(); + if (!name.endsWith(".class")) { + continue; + } + matches.add(name.substring(0, name.lastIndexOf('.')).replace('/', '.')); + } + + for (String match : matches) { + try { + Class loaded = loader.loadClass(match); + if (clazz.isAssignableFrom(loaded)) { + classes.add(loaded.asSubclass(clazz)); + } + } catch (NoClassDefFoundError ignored) { + } + } + } + if (classes.isEmpty()) { + loader.close(); + return null; + } + return classes.get(0); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/ConfigUtils.java b/plugin/src/main/java/net/momirealms/customcrops/utils/ConfigUtils.java new file mode 100644 index 0000000..5ee4bd2 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/utils/ConfigUtils.java @@ -0,0 +1,413 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.utils; + +import com.google.common.base.Preconditions; +import net.momirealms.customcrops.api.CustomCropsPlugin; +import net.momirealms.customcrops.api.common.Pair; +import net.momirealms.customcrops.api.manager.PlaceholderManager; +import net.momirealms.customcrops.api.mechanic.action.Action; +import net.momirealms.customcrops.api.mechanic.action.ActionTrigger; +import net.momirealms.customcrops.api.mechanic.condition.Condition; +import net.momirealms.customcrops.api.mechanic.condition.DeathConditions; +import net.momirealms.customcrops.api.mechanic.item.BoneMeal; +import net.momirealms.customcrops.api.mechanic.item.FertilizerType; +import net.momirealms.customcrops.api.mechanic.item.ItemCarrier; +import net.momirealms.customcrops.api.mechanic.item.water.PassiveFillMethod; +import net.momirealms.customcrops.api.mechanic.item.water.PositiveFillMethod; +import net.momirealms.customcrops.api.mechanic.misc.Value; +import net.momirealms.customcrops.api.mechanic.requirement.Requirement; +import net.momirealms.customcrops.api.mechanic.world.level.WorldSetting; +import net.momirealms.customcrops.api.util.LogUtils; +import net.momirealms.customcrops.mechanic.item.impl.CropConfig; +import net.momirealms.customcrops.mechanic.misc.value.ExpressionValue; +import net.momirealms.customcrops.mechanic.misc.value.PlainValue; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; + +public class ConfigUtils { + + private ConfigUtils() {} + + public static YamlConfiguration getConfig(String file) { + File config = new File(CustomCropsPlugin.get().getDataFolder(), file); + if (!config.exists()) { + CustomCropsPlugin.get().saveResource(file, false); + addDefaultNamespace(config); + } + return YamlConfiguration.loadConfiguration(config); + } + + public static WorldSetting getWorldSettingFromSection(ConfigurationSection section) { + return WorldSetting.of( + section.getBoolean("enable", true), + section.getInt("min-tick-unit", 300), + getRandomTickModeByString(section.getString("crop.mode")), + section.getInt("crop.tick-interval", 1), + getRandomTickModeByString(section.getString("pot.mode")), + section.getInt("pot.tick-interval", 2), + getRandomTickModeByString(section.getString("sprinkler.mode")), + section.getInt("sprinkler.tick-interval", 2), + section.getBoolean("offline-grow", false), + section.getBoolean("season.enable", false), + section.getBoolean("season.auto-alternation", false), + section.getInt("season.duration", 28), + section.getInt("crop.max-per-chunk", 128), + section.getInt("pot.max-per-chunk", -1), + section.getInt("sprinkler.max-per-chunk", 32), + section.getInt("random-tick-speed", 0) + ); + } + + public static boolean getRandomTickModeByString(String str) { + if (str == null) { + return false; + } + if (str.equalsIgnoreCase("RANDOM_TICK")) { + return true; + } + if (str.equalsIgnoreCase("SCHEDULED_TICK")) { + return false; + } + throw new IllegalArgumentException("Invalid mode found when loading world settings: " + str); + } + + public static boolean isVanillaItem(String item) { + char[] chars = item.toCharArray(); + for (char character : chars) { + if ((character < 65 || character > 90) && character != 95) { + return false; + } + } + return true; + } + + /** + * Converts an object into an ArrayList of strings. + * + * @param object The input object + * @return An ArrayList of strings + */ + @SuppressWarnings("unchecked") + public static ArrayList stringListArgs(Object object) { + ArrayList list = new ArrayList<>(); + if (object instanceof String member) { + list.add(member); + } else if (object instanceof List members) { + list.addAll((Collection) members); + } else if (object instanceof String[] strings) { + list.addAll(List.of(strings)); + } + return list; + } + + /** + * Converts an object into a double value. + * + * @param arg The input object + * @return A double value + */ + public static double getDoubleValue(Object arg) { + if (arg instanceof Double d) { + return d; + } else if (arg instanceof Integer i) { + return Double.valueOf(i); + } + return 0; + } + + /** + * Converts an object into a "value". + * + * @param arg int / double / expression + * @return Value + */ + public static Value getValue(Object arg) { + if (arg instanceof Integer i) { + return new PlainValue(i); + } else if (arg instanceof Double d) { + return new PlainValue(d); + } else if (arg instanceof String s) { + return new ExpressionValue(s); + } + throw new IllegalArgumentException("Illegal value type"); + } + + public static double getExpressionValue(Player player, String formula, Map vars) { + formula = PlaceholderManager.getInstance().parse(player, formula, vars); + return new ExpressionBuilder(formula).build().evaluate(); + } + + /** + * Splits a string into a pair of integers using the "~" delimiter. + * + * @param value The input string + * @return A Pair of integers + */ + public static Pair splitStringIntegerArgs(String value, String regex) { + String[] split = value.split(regex); + return Pair.of(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + } + + public static List getFilesRecursively(File folder) { + List ymlFiles = new ArrayList<>(); + if (folder != null && folder.isDirectory()) { + File[] files = folder.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + ymlFiles.addAll(getFilesRecursively(file)); + } else if (file.getName().endsWith(".yml")) { + ymlFiles.add(file); + } + } + } + } + return ymlFiles; + } + + public static Action[] getActions(ConfigurationSection section) { + return CustomCropsPlugin.get().getActionManager().getActions(section); + } + + + public static HashMap getActionMap(ConfigurationSection section) { + HashMap map = new HashMap<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + try { + ActionTrigger trigger = ActionTrigger.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); + map.put(trigger, getActions(innerSection)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + } + } + return map; + } + + public static PositiveFillMethod[] getPositiveFillMethods(ConfigurationSection section) { + ArrayList methods = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + PositiveFillMethod fillMethod = new PositiveFillMethod( + Preconditions.checkNotNull(innerSection.getString("target"), "fill-method target should not be null"), + innerSection.getInt("amount", 1), + getActions(innerSection.getConfigurationSection("actions")), + getRequirements(innerSection.getConfigurationSection("requirements")) + ); + methods.add(fillMethod); + } + } + } + return methods.toArray(new PositiveFillMethod[0]); + } + + public static PassiveFillMethod[] getPassiveFillMethods(ConfigurationSection section) { + ArrayList methods = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + PassiveFillMethod fillMethod = new PassiveFillMethod( + Preconditions.checkNotNull(innerSection.getString("item"), "fill-method item should not be null"), + innerSection.getInt("item-amount", 1), + innerSection.getString("return"), + innerSection.getInt("return-amount", 1), + innerSection.getInt("amount", 1), + getActions(innerSection.getConfigurationSection("actions")), + getRequirements(innerSection.getConfigurationSection("requirements")) + ); + methods.add(fillMethod); + } + } + } + return methods.toArray(new PassiveFillMethod[0]); + } + + public static HashMap getInt2IntMap(ConfigurationSection section) { + HashMap map = new HashMap<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + try { + int i1 = Integer.parseInt(entry.getKey()); + if (entry.getValue() instanceof Integer i2) { + map.put(i1, i2); + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + return map; + } + + public static Requirement[] getRequirements(ConfigurationSection section) { + return CustomCropsPlugin.get().getRequirementManager().getRequirements(section, true); + } + + public static HashMap> getFertilizedPotMap(ConfigurationSection section) { + HashMap> map = new HashMap<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + FertilizerType type = switch (entry.getKey()) { + case "quality" -> FertilizerType.QUALITY; + case "yield-increase" -> FertilizerType.YIELD_INCREASE; + case "variation-increase" -> FertilizerType.VARIATION; + case "soil-retain" -> FertilizerType.SOIL_RETAIN; + case "speed-grow" -> FertilizerType.SPEED_GROW; + default -> null; + }; + if (type != null) { + map.put(type, Pair.of( + Preconditions.checkNotNull(innerSection.getString("dry"), entry.getKey() + ".dry should not be null"), + Preconditions.checkNotNull(innerSection.getString("wet"), entry.getKey() + ".wet should not be null") + )); + } + } + } + } + return map; + } + + public static double[] getQualityRatio(String str) { + String[] split = str.split("/"); + double[] ratio = new double[split.length]; + double weightTotal = Arrays.stream(split).mapToInt(Integer::parseInt).sum(); + double temp = 0; + for (int i = 0; i < ratio.length; i++) { + temp += Integer.parseInt(split[i]); + ratio[i] = temp / weightTotal; + } + return ratio; + } + + public static List> getIntChancePair(ConfigurationSection section) { + ArrayList> pairs = new ArrayList<>(); + if (section != null) { + for (String point : section.getKeys(false)) { + Pair pair = new Pair<>(section.getDouble(point), Integer.parseInt(point)); + pairs.add(pair); + } + } + return pairs; + } + + public static BoneMeal[] getBoneMeals(ConfigurationSection section) { + ArrayList boneMeals = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection innerSection) { + BoneMeal boneMeal = new BoneMeal( + Preconditions.checkNotNull(innerSection.getString("item"), "Bone meal item can't be null"), + innerSection.getInt("item-amount",1), + innerSection.getString("return"), + innerSection.getInt("return-amount",1), + getIntChancePair(innerSection.getConfigurationSection("chance")), + getActions(innerSection.getConfigurationSection("actions")) + ); + boneMeals.add(boneMeal); + } + } + } + return boneMeals.toArray(new BoneMeal[0]); + } + + public static DeathConditions[] getDeathConditions(ConfigurationSection section, ItemCarrier original) { + ArrayList conditions = new ArrayList<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection inner) { + DeathConditions deathConditions = new DeathConditions( + getConditions(inner.getConfigurationSection("conditions")), + inner.getString("model"), + Optional.ofNullable(inner.getString("type")).map(ItemCarrier::valueOf).orElse(original), + inner.getInt("delay", 0) + ); + conditions.add(deathConditions); + } + } + } + return conditions.toArray(new DeathConditions[0]); + } + + public static Condition[] getConditions(ConfigurationSection section) { + return CustomCropsPlugin.get().getConditionManager().getConditions(section); + } + + public static HashMap getStageConfigs(ConfigurationSection section) { + HashMap map = new HashMap<>(); + if (section != null) { + for (Map.Entry entry : section.getValues(false).entrySet()) { + if (entry.getValue() instanceof ConfigurationSection inner) { + try { + int point = Integer.parseInt(entry.getKey()); + if (point < 0) { + LogUtils.warn(entry.getKey() + " is not a valid number"); + } else { + map.put(point, new CropConfig.CropStageConfig( + inner.getString("model"), + point, + inner.getDouble("hologram-offset-correction", 0d), + getActionMap(inner.getConfigurationSection("events")), + getRequirements(inner.getConfigurationSection("requirements.interact")), + getRequirements(inner.getConfigurationSection("requirements.break")) + )); + } + } catch (NumberFormatException e) { + LogUtils.warn(entry.getKey() + " is not a valid number"); + } + } + } + } + return map; + } + + public static void addDefaultNamespace(File file) { + boolean has = Bukkit.getPluginManager().getPlugin("ItemsAdder") != null; + String line; + StringBuilder sb = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + while ((line = reader.readLine()) != null) { + sb.append(line).append(System.lineSeparator()); + } + } catch (IOException e) { + e.printStackTrace(); + } + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { + String finalStr = sb.toString(); + if (!has) { + finalStr = finalStr.replace("CHORUS", "TRIPWIRE").replace("", ""); + } + writer.write(finalStr.replace("{0}", has ? "customcrops:" : "")); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/EventUtils.java b/plugin/src/main/java/net/momirealms/customcrops/utils/EventUtils.java new file mode 100644 index 0000000..02e02da --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/utils/EventUtils.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.utils; + +import org.bukkit.Bukkit; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; + +public class EventUtils { + + public static void fireAndForget(Event event) { + Bukkit.getPluginManager().callEvent(event); + } + + public static boolean fireAndCheckCancel(Event event) { + if (!(event instanceof Cancellable cancellable)) + throw new IllegalArgumentException("Only cancellable events are allowed here"); + Bukkit.getPluginManager().callEvent(event); + return cancellable.isCancelled(); + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java b/plugin/src/main/java/net/momirealms/customcrops/utils/FakeEntityUtils.java similarity index 68% rename from plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java rename to plugin/src/main/java/net/momirealms/customcrops/utils/FakeEntityUtils.java index a0df288..7eb9fcf 100644 --- a/plugin/src/main/java/net/momirealms/customcrops/util/FakeEntityUtils.java +++ b/plugin/src/main/java/net/momirealms/customcrops/utils/FakeEntityUtils.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package net.momirealms.customcrops.util; +package net.momirealms.customcrops.utils; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.events.PacketContainer; @@ -23,31 +23,15 @@ import com.comphenix.protocol.wrappers.*; import com.google.common.collect.Lists; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.momirealms.customcrops.CustomCrops; -import net.momirealms.customcrops.api.object.ItemMode; -import net.momirealms.customcrops.api.object.hologram.TextDisplayMeta; +import net.momirealms.customcrops.api.manager.VersionManager; import org.bukkit.Location; import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import java.util.*; public class FakeEntityUtils { - public static void playWaterAnimation(Player player, Location location, String animation_id, int duration, ItemMode itemMode) { - int id = new Random().nextInt(Integer.MAX_VALUE); - if (itemMode == ItemMode.ARMOR_STAND) { - CustomCrops.getProtocolManager().sendServerPacket(player, getSpawnPacket(id, location, EntityType.ARMOR_STAND)); - CustomCrops.getProtocolManager().sendServerPacket(player, getVanishArmorStandMetaPacket(id)); - CustomCrops.getProtocolManager().sendServerPacket(player, getEquipPacket(id, CustomCrops.getInstance().getIntegrationManager().build(animation_id))); - } else if (itemMode == ItemMode.ITEM_DISPLAY) { - CustomCrops.getProtocolManager().sendServerPacket(player, getSpawnPacket(id, location, EntityType.ITEM_DISPLAY)); - CustomCrops.getProtocolManager().sendServerPacket(player, getItemDisplayMetaPacket(id, CustomCrops.getInstance().getIntegrationManager().build(animation_id))); - } - CustomCrops.getInstance().getScheduler().runTaskAsyncLater(() -> CustomCrops.getProtocolManager().sendServerPacket(player, getDestroyPacket(id)), 1000L * duration); - } - public static WrappedDataWatcher createInvisibleDataWatcher() { WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); //wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(3, WrappedDataWatcher.Registry.get(Boolean.class)), false); @@ -76,7 +60,7 @@ public class FakeEntityUtils { public static PacketContainer getVanishArmorStandMetaPacket(int id) { PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); metaPacket.getIntegers().write(0, id); - if (CustomCrops.getInstance().getVersionHelper().isVersionNewerThan1_19_R2()) { + if (VersionManager.isHigherThan1_19_R2()) { WrappedDataWatcher wrappedDataWatcher = createInvisibleDataWatcher(); setWrappedDataValue(metaPacket, wrappedDataWatcher); } else { @@ -115,7 +99,7 @@ public class FakeEntityUtils { wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(0, WrappedDataWatcher.Registry.get(Byte.class)), mask1); wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(15, WrappedDataWatcher.Registry.get(Byte.class)), mask2); metaPacket.getModifier().write(0, id); - if (CustomCrops.getInstance().getVersionHelper().isVersionNewerThan1_19_R2()) { + if (VersionManager.isHigherThan1_19_R2()) { setWrappedDataValue(metaPacket, wrappedDataWatcher); } else { metaPacket.getWatchableCollectionModifier().write(0, wrappedDataWatcher.getWatchableObjects()); @@ -132,23 +116,6 @@ public class FakeEntityUtils { return metaPacket; } - public static PacketContainer getTextDisplayMetaPacket(int id, Component component, TextDisplayMeta textDisplayMeta) { - PacketContainer metaPacket = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA); - metaPacket.getModifier().write(0, id); - WrappedDataWatcher wrappedDataWatcher = new WrappedDataWatcher(); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(22, WrappedDataWatcher.Registry.getChatComponentSerializer(false)), WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component))); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(24, WrappedDataWatcher.Registry.get(Integer.class)), textDisplayMeta.backgroundColor()); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(14, WrappedDataWatcher.Registry.get(Byte.class)), (byte) 3); - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(25, WrappedDataWatcher.Registry.get(Byte.class)), textDisplayMeta.opacity()); - int mask = 0; - if (textDisplayMeta.hasShadow()) mask += 1; - if (textDisplayMeta.isSeeThrough()) mask += 2; - if (textDisplayMeta.useDefaultBackground()) mask += 4; - wrappedDataWatcher.setObject(new WrappedDataWatcher.WrappedDataWatcherObject(26, WrappedDataWatcher.Registry.get(Byte.class)), (byte) mask); - setWrappedDataValue(metaPacket, wrappedDataWatcher); - return metaPacket; - } - private static void setWrappedDataValue(PacketContainer metaPacket, WrappedDataWatcher wrappedDataWatcher) { List wrappedDataValueList = Lists.newArrayList(); wrappedDataWatcher.getWatchableObjects().stream().filter(Objects::nonNull).forEach(entry -> { diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/ItemUtils.java b/plugin/src/main/java/net/momirealms/customcrops/utils/ItemUtils.java new file mode 100644 index 0000000..da89378 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/utils/ItemUtils.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.utils; + +import de.tr7zw.changeme.nbtapi.NBTItem; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; + +public class ItemUtils { + + public static void giveItem(Player player, ItemStack itemStack, int amount) { + PlayerInventory inventory = player.getInventory(); + ItemMeta meta = itemStack.getItemMeta(); + int maxStackSize = itemStack.getMaxStackSize(); + for (ItemStack other : inventory.getStorageContents()) { + if (other != null) { + if (other.getType() == itemStack.getType() && other.getItemMeta().equals(meta)) { + if (other.getAmount() < maxStackSize) { + int delta = maxStackSize - other.getAmount(); + if (amount > delta) { + other.setAmount(maxStackSize); + amount -= delta; + } else { + other.setAmount(amount + other.getAmount()); + return; + } + } + } + } + } + if (amount > 0) { + for (ItemStack other : inventory.getStorageContents()) { + if (other == null) { + if (amount > maxStackSize) { + amount -= maxStackSize; + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + inventory.addItem(cloned); + } else { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(amount); + inventory.addItem(cloned); + return; + } + } + } + } + if (amount > 0) { + for (int i = 0; i < amount / maxStackSize; i++) { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(maxStackSize); + player.getWorld().dropItem(player.getLocation(), cloned); + } + int left = amount % maxStackSize; + if (left != 0) { + ItemStack cloned = itemStack.clone(); + cloned.setAmount(left); + player.getWorld().dropItem(player.getLocation(), cloned); + } + } + } + + public static void increaseDurability(ItemStack itemStack, int amount) { + if (itemStack == null || itemStack.getType() == Material.AIR) + return; + NBTItem nbtItem = new NBTItem(itemStack); + if (nbtItem.getByte("Unbreakable") == 1) { + return; + } + int damage = Math.max(nbtItem.getInteger("Damage") - amount, 0); + nbtItem.setInteger("Damage", damage); + itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); + } + + public static void decreaseDurability(Player player, ItemStack itemStack, int amount) { + if (itemStack == null || itemStack.getType() == Material.AIR) + return; + NBTItem nbtItem = new NBTItem(itemStack); + ItemMeta previousMeta = itemStack.getItemMeta().clone(); + PlayerItemDamageEvent itemDamageEvent = new PlayerItemDamageEvent(player, itemStack, amount, amount); + Bukkit.getPluginManager().callEvent(itemDamageEvent); + if (!itemStack.getItemMeta().equals(previousMeta) || itemDamageEvent.isCancelled()) { + return; + } + int unBreakingLevel = itemStack.getEnchantmentLevel(Enchantment.DURABILITY); + if (Math.random() > (double) 1 / (unBreakingLevel + 1)) { + return; + } + if (nbtItem.getByte("Unbreakable") == 1) { + return; + } + int damage = nbtItem.getInteger("Damage") + amount; + if (damage > itemStack.getType().getMaxDurability()) { + itemStack.setAmount(0); + } else { + nbtItem.setInteger("Damage", damage); + itemStack.setItemMeta(nbtItem.getItem().getItemMeta()); + } + } +} diff --git a/plugin/src/main/java/net/momirealms/customcrops/utils/RandomUtils.java b/plugin/src/main/java/net/momirealms/customcrops/utils/RandomUtils.java new file mode 100644 index 0000000..93da139 --- /dev/null +++ b/plugin/src/main/java/net/momirealms/customcrops/utils/RandomUtils.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) <2022> + * + * 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 + * 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 . + */ + +package net.momirealms.customcrops.utils; + +import java.util.concurrent.ThreadLocalRandom; + +public class RandomUtils { + + private static final ThreadLocalRandom randomSource = ThreadLocalRandom.current(); + + public static int getRandomInt(int from, int to) { + return randomSource.nextInt(from, to); + } +} diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 21fb2a2..fc20fb6 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,132 +1,145 @@ # Don't change -config-version: '34' +config-version: '35' + +# Debug +debug: false + # BStats metrics: true -# Language: english / french / spanish / chinese / turkish / russian -lang: english -# Debug -debug: - log-world-state: false - log-scheduler: false - log-corruption-fixer: false +# Check updates +update-checker: true + +# Language +lang: english + +# NOTE: This section requires a restart to apply +# NOTE: This section requires a restart to apply +# NOTE: This section requires a restart to apply worlds: - # This is designed for servers that using a separate folder for worlds - # 如果你的服务器使用独立文件夹存储世界,请在此填入绝对地址 + # This is designed for servers that using an independent folder for worlds + # Especially for realm systems absolute-world-folder-path: '' + # A list of worlds that would decide where the plugin mechanisms take effect # Mode: whitelist/blacklist - # Requires a restart when changing mode or editing world list for the safety of data - # 模式:白名单/黑名单 - # 修改本栏需要重启服务器 mode: blacklist list: - blacklist_world - -schedule-system: - # If disabled, you can do that manually with command /customcrops force [consume/sprinklerwork/cropgrow] or API methods - # 如果禁用,你可以使用指令/customcrops force [consume/sprinklerwork/cropgrow]手动操作或使用API - enable: true - # The average interval for a crop to gain one point (measured in seconds) - # 平均每个农作物获得一生长点的时间间隔(秒) - point-gain-interval: 300 - # Water amount and fertilizer would reduce every 3 points are gained - # set it to -1 if you don't want CustomCrops to control consumption/sprinkler tasks - # 默认每3个生长点进行一次水分、肥料消耗 - # 设置为-1以禁用CustomCrops对水分、肥料消耗/洒水器任务的掌管 - consume-water-fertilizer-every-x-point: 3 - # Sprinkler would work every 2 points are gained - sprinkler-work-every-x-point: 2 - # Save cache to file interval (seconds) - # set "-1" to disable - # 保存缓存的时间间隔 (秒) - cache-save-interval: 12000 - # Thread pool settings - # 线程池设置 - thread-pool-settings: - # 核心线程池大小 - corePoolSize: 1 - # 最大线程池大小 - maximumPoolSize: 4 - # seconds (秒) - keepAliveTime: 10 - -optimization: - # Recommend enabling this to prevent large quantities of crops/item frames from lagging the server - # When you are using both item_frame mode and ItemsAdder, you should set "max-furniture-vehicles-per-chunk" in ItemsAdder's config.yml to a higher value - # 推荐启用区块限制来防止玩家种植大量农作物 - # 如果你使用ItemsAdder的展示框模式,你需要在ItemsAdder的配置文件中设置"max-furniture-vehicles-per-chunk"到较高值,否则农作物种植后会消失 - limitation: - # Maximum amount of growing crops per chunk - # Note: This only limits **growing** crops - # If a crop is ripe, it would not be counted - # 每个区块的限制数量 - growing-crop-amount: + - world_nether + - world_the_end + settings: + _DEFAULT_: + # Whether to enable the plugin's pre-made system + # Disabling this option will make all mechanisms stop counting ticks unless you have full control over it using the API. + # If you don't know how to code with CustomCropsAPI, just keep it true, because it is enough to meet the needs of 99% of users. enable: true - default: 48 - # settings for a specified world - # 指定世界的设置 - worlds: - - world:64 - # Experimental option (Only works when only-work-in-loaded-chunks is false) - # This option would make the plugin no longer invoke the unloaded chunks to replace crop models/pots - # Crops/Pots would only be updated during the chunk loading - # 实验性选项,启用以后农作物/种植盆的模型只会在区块加载时得到更新,不再异步加载区块替换模型 - # 此选项仅在only-work-in-loaded-chunks为false时候生效 - only-update-during-chunk-loading: false + # Season mechanisms + season: + enable: true + # Enable automatic seasonal change + auto-alternation: true + # Game days of each season + duration: 28 + # Random tick speed + # It's different from the vanilla RandomTickSpeed. + # CustomCrops' random tick speed has a larger base value + # So we can have more precise control on crops' growth speed + random-tick-speed: 20 + # The smallest tick unit in seconds used in scheduled tick mode + # 300s means that a crop would be certainly ticked once in 300s + # For crops, under the same conditions, the growth rate of crops is basically the same + # For sprinklers and pots, they would work periodically. + min-tick-unit: 300 + # Settings for crops + crop: + # [RANDOM_TICK] + # Random tick mode is similar to the vanilla one where crops grow randomly + # Due to randomness, crops grown under the same conditions may have significant stage differences + # [SCHEDULED_TICK] + # Scheduler mode provides more reliable growth schedule management + # which allows crops to grow at almost the same speed + mode: RANDOM_TICK + tick-interval: 1 + # Limit the max amount of crops in one chunk + max-per-chunk: 150 + # Settings for pots + pot: + # RANDOM_TICK / SCHEDULED_TICK + mode: SCHEDULED_TICK + tick-interval: 3 + # Limit the max amount of pots in one chunk + max-per-chunk: -1 + # Settings for sprinklers + sprinkler: + # RANDOM_TICK / SCHEDULED_TICK + mode: SCHEDULED_TICK + tick-interval: 2 + # Limit the max amount of sprinklers in one chunk + max-per-chunk: 20 + # Offline growth is a special mode for crops + # Take server performance into account, enabling this option will not load the unloaded chunks + # On the contrary, CustomCrops get how long crops have grown by comparing the unload and load timestamps + offline-growth: + enable: false + # Maximum offline time recorded in seconds + # Please do not set this option to a value that is too large, + # as it may cause chunks that have been unloaded for a long time + # to consume too much server performance during loading + max-offline-seconds: 1200 + _WORLDS_: + world_nether: + enable: false + world_the_end: + enable: false +# Mechanics settings mechanics: - # Does the system only work in loaded chunks (Requires you to stop the server before changing this setting) - # 插件是否只在加载中的区块工作 (需要关闭服务器再设置此项) - only-work-in-loaded-chunks: true + # You can create more ranks by adding more "/" for instance x/x/x/x/x + # "x" is weight, represents the proportion of probability # 17/2/1 = 85%/10%/5% - # 2/2/1 = 40%/40%/20% - # You can customize more ranks like x/x/x/x/x + # 85% = 17/(17+2+1) * 100% default-quality-ratio: 17/2/1 - # Season mechanic - # 季节机制 - season: + + # Scarecrow prevents crops from being attacked by crows + scarecrow: enable: true - # If you want all the worlds to share the same season - # 是否全世界同步季节 - sync-season: - enable: false - reference: world - # 自动季节交替 - auto-season-change: - enable: true - # duration of each season (Minecraft days) - # 每个季节的时长 - duration: 28 - greenhouse: - enable: true - block: customcrops:greenhouse_glass - # effective range (Y axis) - # Y轴有效范围 - range: 5 - # scarecrow item id - # 稻草人物品id - scarecrow: customcrops:scarecrow + id: {0}scarecrow + range: 7 + + # Greenhouse glass prevents crops from withering from season changing + greenhouse: + enable: true + id: {0}greenhouse_glass + range: 5 + + # Sync seasons + sync-season: + enable: true + reference: world + # Vanilla farmland settings - # 原版耕地设置 vanilla-farmland: - # disable vanilla farmland moisture mechanics - # 禁用原版耕地湿润机制 + # Disable vanilla farmland moisture mechanics + # This option exists because some users prefer to use the vanilla farmland but the water system conflicts with the vanilla one disable-moisture-mechanic: false - # prevent entities from trampling the farmland - # 防止玩家踩踏耕地 + # Prevent entities from trampling the farmland prevent-trampling: false other-settings: - # This option requires a skill-plugin hook - # Which would increase the amount of crops player get - # 这需要一个技能插件才能生效,可以根据玩家种植技能等级提供增益 - skill-bonus: - enable: false - formula: '{base} + {level} * 0.01' - # MMOCore profession name - # MMOCore职业名 - MMOCore-profession-name: 'farmer' - # Enable corruption fixer - enable-corruption-fixer: false - # Fixed corrupted pots when interacting - corrupt-fix-range: 4 + # It's recommended to use MiniMessage format. If you insist on using legacy color code "&", enable the support below. + # Disable this would improve performance + legacy-color-code-support: true + + # Thread pool settings + thread-pool-settings: + corePoolSize: 10 + maximumPoolSize: 10 + keepAliveTime: 30 + + # Using items from other plugins + item-detection-order: [] + + # Whether to protect the original lore of the item + # This uses the scoreboard component to identify the plugin's lore, + # which may conflict with some plugins that still use SpigotAPI#ItemMeta. + protect-original-lore: false \ No newline at end of file diff --git a/plugin/src/main/resources/contents/crops/default.yml b/plugin/src/main/resources/contents/crops/default.yml new file mode 100644 index 0000000..6107aec --- /dev/null +++ b/plugin/src/main/resources/contents/crops/default.yml @@ -0,0 +1,285 @@ +# ID +tomato: + # Item type + # TRIPWIRE / ITEM_FRAME / ITEM_DISPLAY (1.19.4+) + type: TRIPWIRE + # The crop can only be planted on whitelisted pots + pot-whitelist: + - default + # Seed of the crop + seed: {0}tomato_seeds + # Only works in ITEM_FRAME / ITEM_DISPLAY mode + random-rotation: true + # Break/Plant requirements + requirements: + break: {} + plant: + requirement_1: + type: season + value: + - Spring + - Autumn + not-met-actions: + message_action: + type: message + value: "It's not a good season to plant tomato" + # Event settings + events: + plant: + # Swing the main hand + swing_hand_action: + type: swing-hand + value: true + # Send sound while planting + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + break: + sound_action: + type: sound + value: + source: player + key: minecraft:block.crop.break + volume: 1 + pitch: 1 + # This determines the maximum time for crops to grow + max-points: 6 + # This section determines the actions of crops at each point + points: + 0: + # If model is set, the crop will replace its appearance when it reaches this stage. + model: {0}tomato_stage_1 + # Each crop has a different model height, so appropriate offsets should be made when displaying holograms. + hologram-offset-correction: 0.2 + # Event settings + events: + break: + # 30% chance of dropping crop seeds + action_1: + type: drop-items + value: + ignore-fertilizer: true + item: {0}tomato_seeds + min: 1 + max: 1 + chance: 0.3 + 1: + model: {0}tomato_stage_2 + hologram-offset-correction: 0.2 + events: + break: + action_1: + type: drop-items + value: + ignore-fertilizer: true + item: {0}tomato_seeds + min: 1 + max: 1 + chance: 0.5 + 3: + model: {0}tomato_stage_3 + hologram-offset-correction: 0.2 + events: + break: + action_1: + type: drop-items + value: + ignore-fertilizer: true + item: {0}tomato_seeds + min: 1 + max: 1 + chance: 0.7 + 5: + model: {0}tomato_stage_4 + hologram-offset-correction: 0.2 + events: + interact: + action_1: + type: conditional + value: + conditions: + requirement_1: + type: item-in-hand + value: + item: "AIR" + actions: + action_1: + type: break + value: true + action_2: + type: swing-hand + value: true + action_3: + type: plant + value: + point: 0 + crop: tomato + grow: + action_1: + type: variation + value: + golden: + item: {0}tomato_stage_golden + type: TRIPWIRE + chance: 0.01 + break: + action_1: + type: drop-items + value: + ignore-fertilizer: true + item: {0}tomato_seeds + min: 1 + max: 2 + chance: 1 + action_2: + type: quality-crops + value: + min: 1 + max: 4 + items: + 1: {0}tomato + 2: {0}tomato_silver_star + 3: {0}tomato_golden_star + 6: + events: + grow: + action_1: + type: variation + value: + gigantic: + item: {0}gigantic_tomato + type: CHORUS + chance: 0.02 + # The crop would actually not gain so many points + # But you can set some special crops' loots here + 99: + model: {0}tomato_stage_golden + requirements: + break: + requirement_1: + type: item-in-hand + value: + item: GOLDEN_HOE + amount: 1 + not-met-actions: + actionbar_action: + type: actionbar + value: "[X] Hold a golden hoe to harvest the golden tomato" + events: + break: + action_1: + type: drop-items + value: + ignore-fertilizer: true + item: {0}tomato_seeds + min: 1 + max: 2 + chance: 1 + action_2: + type: drop-items + value: + ignore-fertilizer: false + item: {0}golden_tomato + min: 1 + max: 4 + chance: 1 + action_3: + type: durability + value: -1 + interact: + action_1: + type: conditional + value: + conditions: + requirement_1: + type: item-in-hand + value: + item: GOLDEN_HOE + amount: 1 + actions: + action_1: + type: break + value: true + action_2: + type: swing-hand + value: true + action_3: + type: plant + value: + point: 0 + crop: tomato + # Custom grow conditions + grow-conditions: + season_condition: + type: suitable_season + value: + - Spring + - Autumn + water_condition: + type: water_more_than + value: 0 + # Custom death conditions + death-conditions: + no_water: + # You can customize the death stage models according to the reasons + model: {0}crop_stage_death + conditions: + '&&': + condition_1: + type: water_less_than + value: 1 + condition_2: + type: random + value: 0.7 + unsuitable_season: + model: {0}crop_stage_death + conditions: + condition_1: + type: unsuitable_season + value: + - Winter + crow_attack: + # Crop would be removed if "model" is not set + # model: {0}crop_stage_death + conditions: + condition_1: + type: crow_attack + value: + chance: 0.005 + fly-model: {0}crow_fly + stand-model: {0}crow_stand + # delay 150 ticks for the crop to be removed + delay: 150 + # Custom bone meal + custom-bone-meal: + bone_meal_1: + item: BONE_MEAL + actions: + swing_action: + type: swing-hand + value: true + particle_action: + type: particle + value: + particle: VILLAGER_HAPPY + x: 0.5 + y: 0.5 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-y: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.bone_meal.use + volume: 1 + pitch: 1 + chance: + 2: 0.2 + 1: 0.6 \ No newline at end of file diff --git a/plugin/src/main/resources/contents/crops/tomato.yml b/plugin/src/main/resources/contents/crops/tomato.yml deleted file mode 100644 index 1cb0afe..0000000 --- a/plugin/src/main/resources/contents/crops/tomato.yml +++ /dev/null @@ -1,345 +0,0 @@ -# Crop key name -# 农作物配置名 -tomato: - # Crop mode, make sure that type is consistent with IA/Oraxen configs - # Otherwise some unexpected problems would occur - # TRIPWIRE / ITEM_FRAME / ITEM_DISPLAY (1.19.4+) - # 农作物模式,请确保和你的资源包配置等一致,否则会出现bug - type: TRIPWIRE - - # The crop can only be planted on certain pots - # Pot are stored in /CustomCrops/contents/pots - # 农作物只能在指定的种植盆中种植 - pot-whitelist: - - default - - # Crop's seed, if you want to use Vanilla items, just use Capital letters. (Example APPLE) - # 农作物的种子,如果你想用原版物品做种子,就使用大写字母 - seed: customcrops:tomato_seeds - - # Only works in ITEM_FRAME / ITEM_DISPLAY mode - random-rotation: true - - # break/plant requirements - # 破坏/种植农作物的需求 - requirements: - break: - requirement_1: - # Requirement type, you can get a full list of requirements on wiki page - # 需求类型,wiki中有详细的全部需求类型和示范 - type: permission - value: tomato.break - # The message to show when not meeting the requirement(Supports multiple lines) - # 不满足需求的提示,支持多行 - message: "You don't have permission to harvest the tomato" - plant: - requirement_1: - type: permission - value: tomato.plant - message: "You don't have permission to plant the tomato" - requirement_2: - type: season - value: - - Spring - - Autumn - message: "It's not a good season to plant tomato" - - # plant actions - # 种植时发生的动作 - plant-actions: - action_1: - # Requirement type, you can get a full list of actions on wiki page - # 动作类型,wiki中有详细的全部动作类型和示范 - type: sound - value: - source: player - key: minecraft:item.hoe.till - volume: 1 - pitch: 1 - - # This decides when the crop data would be removed from cache - # 这决定了农作物数据什么时候从缓存中移除 - max-points: 6 - # What should a crop do when gaining a certain amount of points - # 农作物在获取指定生长点数时候执行的动作 - points: - 0: - # change the crop's model - # 改变农作物的模型 - model: customcrops:tomato_stage_1 - # As crop's models are different and have various height - # So sometimes the pot information hologram can't show in a proper place - # Set "hologram-offset-correction" to a high value would increase the - # vertical position of the hologram - # 因为不同农作物有不同的模型高度,所以在显示悬浮信息的时候,可能会导致模型遮挡了悬浮信息 - # 因此设置 "hologram-offset-correction" 为更大值可以提升悬浮字的高度,以免遮挡 - hologram-offset-correction: 0.2 - # Available events: break/grow/interact-by-hand/interact-with-item - events: - break: - action_1: - type: drop-items - value: - other-items: - loot_1: - # Other plugin item compatibility(Can also be used in quality-crops) - # MMOItems:TYPE:ID - # MythicMobs:id - item: customcrops:tomato_seeds - min: 1 - max: 1 - # 0-1 - chance: 0.3 - action_2: - type: sound - value: - source: player - key: minecraft:block.crop.break - volume: 1 - pitch: 1 - 1: - model: customcrops:tomato_stage_2 - hologram-offset-correction: 0.2 - events: - break: - action_1: - type: drop-items - value: - other-items: - loot_1: - item: customcrops:tomato_seeds - min: 1 - max: 1 - chance: 0.5 - action_2: - type: sound - value: - source: player - key: minecraft:block.crop.break - volume: 1 - pitch: 1 - 3: - model: customcrops:tomato_stage_3 - hologram-offset-correction: 0.2 - events: - break: - action_1: - type: drop-items - value: - other-items: - loot_1: - item: customcrops:tomato_seeds - min: 1 - max: 1 - chance: 0.8 - action_2: - type: sound - value: - source: player - key: minecraft:block.crop.break - volume: 1 - pitch: 1 - 5: - model: customcrops:tomato_stage_4 - hologram-offset-correction: 0.2 - events: - break: - action_1: - type: sound - value: - source: player - key: minecraft:block.crop.break - volume: 1 - pitch: 1 - action_2: - type: drop-items - value: - quality-crops: - min: 1 - max: 4 - items: - 1: customcrops:tomato - 2: customcrops:tomato_silver_star - 3: customcrops:tomato_golden_star - other-items: - loot_1: - item: customcrops:tomato_seeds - min: 1 - max: 1 - chance: 1 - grow: - action_1: - type: variation - value: - golden: - item: customcrops:tomato_stage_golden - type: TRIPWIRE - chance: 0.01 - interact-by-hand: - action_1: - type: break - value: true # whether to trigger break event too 是否触发break事件 - action_2: - type: replant - value: - point: 0 - crop: tomato # Replant the crop 重新种植农作物 - model: customcrops:tomato_stage_1 # crop model 农作物模型 - interact-with-item: - interact_1: - # the item to use - # 使用的物品 - item: customcrops:i_want_overgrown_tomato_right_now - # whether to reduce the amount - # 是否减少数量 - reduce-amount: true - # the item to return, remove this section if you don't want item back - # 返还的物品,如果你不想要返还物品,请删除此项目 - return: customcrops:returned_item - # actions to perform - # 执行的动作 - actions: - action_1: - type: variation - value: - gigantic: - item: customcrops:gigantic_tomato - type: CHORUS - chance: 1 - 6: - # In this example, the model is not set, so the crop would not change its model - # when reaching this point. But here's a section called variation, which means that - # the crop has a little chance to be gigantic when it's already ripe. Since it reaches - # "max-points", the crop data would be removed from data and no longer affect server performance - # 在这个示范中,model没有被设置,因此,农作物不会在此生长点数时替换模型 - # 但是这里有一个生长时候触发的变异动作,意味着农作物有小概率在此阶段发生变异 - # 因为农作物已经达到了最大生长点数,农作物数据将自此从数据中移除,不再对服务器性能造成影响 - events: - grow: - action_1: - type: variation - value: - gigantic: - item: customcrops:gigantic_tomato - type: CHORUS - chance: 0.02 - - # The crop would actually not gain so many points - # But you can set some special crops' loots here - # 农作物实际上并不会获取这么多生长点数,但是你可以在此设置一些特殊掉落物 - 99: - model: customcrops:tomato_stage_golden - events: - break: - action_1: - type: drop-items - value: - other-items: - loot_1: - item: customcrops:golden_tomato - min: 1 - max: 1 - chance: 1 - loot_2: - item: customcrops:golden_tomato - min: 1 - max: 2 - chance: 0.5 - loot_3: - item: customcrops:golden_tomato - min: 1 - max: 2 - chance: 0.3 - interact-with-item: - interact_1: - item: GOLDEN_HOE - reduce-amount: false - requirements: - requirement_1: - type: permission - value: golden_hoe.use - message: "You don't have permission to use golden hoe." - actions: - action_1: - type: break - action_2: - type: replant - value: - point: 0 - crop: tomato - model: customcrops:tomato_stage_1 - - # Custom grow conditions - # You can get a full list of grow conditions on wiki page. - # The default config tells that the crop would only grow in Spring and Autumn, - # and it requires at least a certain amount of water to grow. - # Though it would not grow in Summer, but it would still be alive. If you want - # the crop to die, see the "death-conditions" below. - # 自定义生长条件,默认配置是:农作物会在春和秋两个季节生长,并且要求种植盆水量大于3 - grow-conditions: - condition_1: - type: suitable_season - value: - - Spring - - Autumn - condition_2: - type: water_more_than - value: 2 - - # Custom death conditions - # 自定义死亡条件 - death-conditions: - no_water: - # You can customize the dead models according to the reasons - # 你可以根据死因设置不同的死亡模型 - model: customcrops:crop_stage_death - conditions: - # "&&" represents "And" - # "||" represents "Or" - # the default config tells that the crop would have 70% chance to die if the pot is dry - # 默认配置:农作物有70%概率在种植盆干燥情况下死亡 - '&&': - condition_1: - type: water_less_than - value: 1 - condition_2: - type: random - value: 0.7 - unsuitable_season: - model: customcrops:crop_stage_death - conditions: - condition_1: - type: unsuitable_season - value: - - Winter - crow_attack: - # Crop would be removed if "model" is not set - # 如果没有设置model,农作物会直接被移除,而不是替换模型 - conditions: - condition_1: - type: crow_attack - value: - chance: 0.001 - fly-model: customcrops:crow_fly - stand-model: customcrops:crow_stand - - # Custom bone meal - # 自定义骨粉 - custom-bone-meal: - bone_meal_1: - item: BONE_MEAL - particle: VILLAGER_HAPPY - sound: minecraft:item.bone_meal.use - # 20% chance to grow 2 points - # (100% - 20%) * 60% = 48% chance to grow 1 point - # 20%概率获得两个生长点数 - # (100% - 20%) * 60% = 48% 概率获得一个生长点数 - chance: - 2: 0.2 - 1: 0.6 - bone_meal_2: - item: customcrops:custom_bone_meal - return: customcrops:custom_bone_meal_used - chance: - 2: 0.4 - 1: 1 \ No newline at end of file diff --git a/plugin/src/main/resources/contents/fertilizers/default.yml b/plugin/src/main/resources/contents/fertilizers/default.yml new file mode 100644 index 0000000..32b73d0 --- /dev/null +++ b/plugin/src/main/resources/contents/fertilizers/default.yml @@ -0,0 +1,711 @@ +quality_1: + type: QUALITY + icon: '뀆' + chance: 1 + times: 28 + ratio: 7/2/1 + item: {0}quality_1 + before-plant: true + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: SCRAPE + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' +quality_2: + type: QUALITY + icon: '뀇' + chance: 1 + times: 28 + ratio: 11/6/3 + item: {0}quality_2 + before-plant: true + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: WAX_OFF + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' +quality_3: + type: QUALITY + icon: '뀈' + chance: 1 + times: 28 + ratio: 2/2/1 + item: {0}quality_3 + before-plant: true + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: WAX_ON + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' +soil_retain_1: + type: SOIL_RETAIN + icon: '뀉' + chance: 0.1 + times: 28 + item: {0}soil_retain_1 + before-plant: false + particle: SPELL + sound: minecraft:item.hoe.till + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: SPELL + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' +soil_retain_2: + type: SOIL_RETAIN + icon: '뀊' + chance: 0.2 + times: 28 + item: {0}soil_retain_2 + before-plant: false + particle: SPELL + sound: minecraft:item.hoe.till + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: SPELL + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' +soil_retain_3: + type: SOIL_RETAIN + icon: '뀋' + chance: 0.3 + times: 28 + item: {0}soil_retain_3 + before-plant: false + particle: SPELL + sound: minecraft:item.hoe.till + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: SPELL + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' +speed_grow_1: + type: SPEED_GROW + icon: '뀌' + times: 14 + item: {0}speed_grow_1 + before-plant: true + pot-whitelist: + - default + chance: + 1: 0.5 + events: + use: + particle_action: + type: particle + value: + particle: SPELL_WITCH + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' +speed_grow_2: + type: SPEED_GROW + icon: '뀍' + times: 14 + item: {0}speed_grow_2 + before-plant: true + particle: SPELL_WITCH + sound: minecraft:item.hoe.till + pot-whitelist: + - default + chance: + 2: 0.1 + 1: 0.7 + events: + use: + particle_action: + type: particle + value: + particle: SPELL_WITCH + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' +speed_grow_3: + type: SPEED_GROW + icon: '뀎' + times: 14 + item: {0}speed_grow_3 + before-plant: true + particle: SPELL_WITCH + sound: minecraft:item.hoe.till + pot-whitelist: + - default + chance: + 3: 0.1 + 2: 0.4 + 1: 0.8 + events: + use: + particle_action: + type: particle + value: + particle: SPELL_WITCH + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' +variation_1: + type: VARIATION + icon: '뀒' + times: 14 + chance: 0.02 + item: {0}variation_1 + before-plant: false + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: SOUL + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' +variation_2: + type: VARIATION + icon: '뀓' + times: 14 + chance: 0.04 + item: {0}variation_2 + before-plant: false + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: SOUL + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' +variation_3: + type: VARIATION + icon: '뀔' + times: 14 + chance: 0.08 + item: {0}variation_3 + before-plant: false + pot-whitelist: + - default + events: + use: + particle_action: + type: particle + value: + particle: SOUL + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' +yield_increase_1: + type: YIELD_INCREASE + icon: '뀏' + times: 14 + item: {0}yield_increase_1 + before-plant: true + pot-whitelist: + - default + chance: + 3: 0.1 + 2: 0.4 + 1: 0.8 + events: + use: + particle_action: + type: particle + value: + particle: TOTEM + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' +yield_increase_2: + type: YIELD_INCREASE + icon: '뀐' + times: 14 + item: {0}yield_increase_2 + before-plant: true + pot-whitelist: + - default + chance: + 3: 0.2 + 2: 0.6 + 1: 1 + events: + use: + particle_action: + type: particle + value: + particle: TOTEM + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' +yield_increase_3: + type: YIELD_INCREASE + icon: '뀑' + times: 14 + item: {0}yield_increase_3 + before-plant: true + pot-whitelist: + - default + chance: + 3: 0.4 + 2: 1 + events: + use: + particle_action: + type: particle + value: + particle: TOTEM + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + sound_action: + type: sound + value: + source: player + key: minecraft:item.hoe.till + volume: 1 + pitch: 1 + wrong_pot: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] This fertilizer can only be used in pots.' + before_plant: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] You can only use this fertilizer before planting the crop.' \ No newline at end of file diff --git a/plugin/src/main/resources/contents/fertilizers/quality.yml b/plugin/src/main/resources/contents/fertilizers/quality.yml deleted file mode 100644 index 8a5b50a..0000000 --- a/plugin/src/main/resources/contents/fertilizers/quality.yml +++ /dev/null @@ -1,39 +0,0 @@ -quality_1: - type: QUALITY - icon: '뀆' - chance: 1 - times: 28 - # 70%/20%/10% - ratio: 7/2/1 - item: customcrops:quality_1 - before-plant: true - particle: SCRAPE - sound: minecraft:item.hoe.till - pot-whitelist: - - default -quality_2: - type: QUALITY - icon: '뀇' - chance: 1 - times: 28 - # 55%/30%/15% - ratio: 11/6/3 - item: customcrops:quality_2 - before-plant: true - particle: SCRAPE - sound: minecraft:item.hoe.till - pot-whitelist: - - default -quality_3: - type: QUALITY - icon: '뀈' - chance: 1 - times: 28 - # 40%/40%/20% - ratio: 2/2/1 - item: customcrops:quality_3 - before-plant: true - particle: SCRAPE - sound: minecraft:item.hoe.till - pot-whitelist: - - default \ No newline at end of file diff --git a/plugin/src/main/resources/contents/fertilizers/soil-retain.yml b/plugin/src/main/resources/contents/fertilizers/soil-retain.yml deleted file mode 100644 index 990659e..0000000 --- a/plugin/src/main/resources/contents/fertilizers/soil-retain.yml +++ /dev/null @@ -1,33 +0,0 @@ -soil_retain_1: - type: SOIL_RETAIN - icon: '뀉' - chance: 0.1 - times: 28 - item: customcrops:soil_retain_1 - before-plant: false - particle: SPELL - sound: minecraft:item.hoe.till - pot-whitelist: - - default -soil_retain_2: - type: SOIL_RETAIN - icon: '뀊' - chance: 0.2 - times: 28 - item: customcrops:soil_retain_2 - before-plant: false - particle: SPELL - sound: minecraft:item.hoe.till - pot-whitelist: - - default -soil_retain_3: - type: SOIL_RETAIN - icon: '뀋' - chance: 0.3 - times: 28 - item: customcrops:soil_retain_3 - before-plant: false - particle: SPELL - sound: minecraft:item.hoe.till - pot-whitelist: - - default \ No newline at end of file diff --git a/plugin/src/main/resources/contents/fertilizers/speed-grow.yml b/plugin/src/main/resources/contents/fertilizers/speed-grow.yml deleted file mode 100644 index b04e394..0000000 --- a/plugin/src/main/resources/contents/fertilizers/speed-grow.yml +++ /dev/null @@ -1,60 +0,0 @@ -speed_grow_1: - type: SPEED_GROW - # Fertilizer Hologram display icon - # 肥料在悬浮字中显示的图标 - icon: '뀌' - # How many days can this fertilizer stay in pot - # 肥料有效时间(游戏日) - times: 14 - # item id - # 物品ID - item: customcrops:speed_grow_1 - # Should this fertilizer be used before planting - # 肥料是否只能在种植前使用 - before-plant: true - # The particle to be displayed when using fertilizer - # 使用肥料的粒子效果 - # https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Particle.html - particle: SPELL_WITCH - # The sound to play when using - # 使用肥料的声音 - sound: minecraft:item.hoe.till - # Pot whitelist - # 种植盆白名单 - pot-whitelist: - - default - # 50% chance to gain another 1 points - chance: - 1: 0.5 -speed_grow_2: - type: SPEED_GROW - icon: '뀍' - times: 14 - item: customcrops:speed_grow_2 - before-plant: true - particle: SPELL_WITCH - sound: minecraft:item.hoe.till - pot-whitelist: - - default - # 10% chance to gain another 2 points - # (100% - 10%) * 70% = 63% chance to gain another 1 point - chance: - 2: 0.1 - 1: 0.7 -speed_grow_3: - type: SPEED_GROW - icon: '뀎' - times: 14 - item: customcrops:speed_grow_3 - before-plant: true - particle: SPELL_WITCH - sound: minecraft:item.hoe.till - pot-whitelist: - - default - # 10% chance to gain another 3 points - # (100% - 10%) * 40% = 36% chance to gain another 2 points - # (100% - (36% + 10%)) * 80% = 43.2% chance to gain another 1 point - chance: - 3: 0.1 - 2: 0.4 - 1: 0.8 \ No newline at end of file diff --git a/plugin/src/main/resources/contents/fertilizers/variation.yml b/plugin/src/main/resources/contents/fertilizers/variation.yml deleted file mode 100644 index c4bb238..0000000 --- a/plugin/src/main/resources/contents/fertilizers/variation.yml +++ /dev/null @@ -1,35 +0,0 @@ -variation_1: - type: VARIATION - icon: '뀒' - times: 14 - # If a crop's default variation chance is 0.01, now it's 0.03 - # 如果农作物的变异概率是0.01,那么使用肥料后变成了0.03 - chance: 0.02 - item: customcrops:variation_1 - before-plant: true - particle: SOUL - sound: minecraft:item.hoe.till - pot-whitelist: - - default -variation_2: - type: VARIATION - icon: '뀓' - times: 14 - chance: 0.04 - item: customcrops:variation_2 - before-plant: true - particle: SOUL - sound: minecraft:item.hoe.till - pot-whitelist: - - default -variation_3: - type: VARIATION - icon: '뀔' - times: 14 - chance: 0.08 - item: customcrops:variation_3 - before-plant: true - particle: SOUL - sound: minecraft:item.hoe.till - pot-whitelist: - - default \ No newline at end of file diff --git a/plugin/src/main/resources/contents/fertilizers/yield-increase.yml b/plugin/src/main/resources/contents/fertilizers/yield-increase.yml deleted file mode 100644 index bb4028a..0000000 --- a/plugin/src/main/resources/contents/fertilizers/yield-increase.yml +++ /dev/null @@ -1,49 +0,0 @@ -yield_increase_1: - type: YIELD_INCREASE - icon: '뀏' - times: 14 - item: customcrops:yield_increase_1 - before-plant: true - particle: TOTEM - sound: minecraft:item.hoe.till - pot-whitelist: - - default - # 10% chance to gain another 3 crops - # (100% - 10%) * 40% = 36% chance to gain another 2 crops - # (100% - (10% + 36%)) * 80% = 43.2% chance to gain another 1 crop - chance: - 3: 0.1 - 2: 0.4 - 1: 0.8 -yield_increase_2: - type: YIELD_INCREASE - icon: '뀐' - times: 14 - item: customcrops:yield_increase_2 - before-plant: true - particle: TOTEM - sound: minecraft:item.hoe.till - pot-whitelist: - - default - # 20% chance to gain another 3 crops - # (100% - 20%) * 60% = 48% chance to gain another 2 crops - # (100% - (20% + 48%)) * 80% = 25.6% chance to gain another 1 crop - chance: - 3: 0.2 - 2: 0.6 - 1: 1 -yield_increase_3: - type: YIELD_INCREASE - icon: '뀑' - times: 14 - item: customcrops:yield_increase_3 - before-plant: true - particle: TOTEM - sound: minecraft:item.hoe.till - pot-whitelist: - - default - # 40% chance to gain another 3 crops - # (100% - 40%) * 100% = 60% chance to gain another 2 crops - chance: - 3: 0.4 - 2: 1 \ No newline at end of file diff --git a/plugin/src/main/resources/contents/pots/default.yml b/plugin/src/main/resources/contents/pots/default.yml index a83263f..f230d54 100644 --- a/plugin/src/main/resources/contents/pots/default.yml +++ b/plugin/src/main/resources/contents/pots/default.yml @@ -1,105 +1,128 @@ -# Pot key name +# ID default: - # The max water storage - # 最大储水量 + # Maximum water storage capacity max-water-storage: 5 + # The most basic settings base: - # basic models, use capital letters if you want to use vanilla blocks - # Example: FARMLAND - # 基础模型,如果你想要使用原版方块请使用大写字母,例如FARMLAND(耕地) - dry: customcrops:dry_pot - wet: customcrops:wet_pot - # If you want fertilized pots to have different looks - # 如果你想让施肥的种植盆有不同的外观 + # Pot models + dry: {0}dry_pot + wet: {0}wet_pot + # Does the pot absorb raindrop + absorb-rainwater: true + # Does nearby water make the pot wet + absorb-nearby-water: false + # Set unique looks for pots with different fertilizer statuses fertilized-pots: enable: false quality: - dry: customcrops:dry_pot - wet: customcrops:wet_pot + dry: {0}dry_pot + wet: {0}wet_pot yield-increase: - dry: customcrops:dry_pot - wet: customcrops:wet_pot + dry: {0}dry_pot + wet: {0}wet_pot variation: - dry: customcrops:dry_pot - wet: customcrops:wet_pot + dry: {0}dry_pot + wet: {0}wet_pot soil-retain: - dry: customcrops:dry_pot - wet: customcrops:wet_pot + dry: {0}dry_pot + wet: {0}wet_pot speed-grow: - dry: customcrops:dry_pot - wet: customcrops:wet_pot - # Methods to fill the pot - # 浇水的方法 + dry: {0}dry_pot + wet: {0}wet_pot + # Methods to fill the watering can fill-method: + # The name of the method, it's totally customized method_1: # The item to use - # 使用的物品 item: WATER_BUCKET - # The item returned - # 返还的物品 + # The item returned (Optional) return: BUCKET - # water amount to add - # 增加的水量 + # The amount of water to add amount: 3 - # 粒子效果 - particle: WATER_SPLASH - # 声音 - sound: minecraft:block.water.ambient + actions: + sound_action: + type: sound + value: + source: player + key: minecraft:item.bucket.fill + volume: 1 + pitch: 1 + swing_hand_action: + type: swing-hand + value: true method_2: - item: WATER + item: POTION return: GLASS_BOTTLE amount: 1 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient - method_3: - item: SNOWBALL - amount: 1 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient - method_4: - # This is just an example - # "customcrops:magic_water" actually not exists - # 这只是个示例, "customcrops:magic_water"事实上不存在 - item: customcrops:magic_water - amount: 5 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient - # Pot info hologram - # 种植盆悬浮信息 - hologram: - # ARMOR_STAND / TEXT_DISPLAY (1.19.4+) - # 显示载体 - type: ARMOR_STAND - # in seconds - # 持续时间 - duration: 2 - # remove this if you want players can see the pot info even with empty hands - # 如果你想要空手也能查看种植盆信息,那么移除这个项目 - require-item: customcrops:soil_surveyor - # Fertilizer info - # 肥料信息 - fertilizer: - enable: true - vertical-offset: 0.8 - content: '{icon} {left_times}/{max_times}' - # Water info - # 水分信息 - water: - enable: true - vertical-offset: 1.05 - # Available placeholders - # {current} current water 当前水量 - # {storage} max water 最大储水量 - # {water_bar} water bar image 水条 - content: '{water_bar}' - water-bar: - left: '뀂' - full: '뀁뀃' - empty: '뀁뀄' - right: '뀁뀅' - text-display-options: - background-color: 0,0,0,0 - has-shadow: false - is-see-through: false - use-default-background-color: false - text-opacity: -1 \ No newline at end of file + actions: + sound_action: + type: sound + value: + source: player + key: minecraft:item.bottle.fill + volume: 1 + pitch: 1 + swing_hand_action: + type: swing-hand + value: true + # Water bar image settings + water-bar: + left: '뀂' + full: '뀁뀃' + empty: '뀁뀄' + right: '뀁뀅' + # Event settings + events: + add_water: + particle_action: + type: particle + value: + particle: WATER_SPLASH + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + offset-x: 0.3 + offset-z: 0.3 + interact: + # This action allows to display hologram while holding the specified item. + conditional_action: + type: conditional + value: + conditions: + requirement_1: + type: item-in-hand + value: + hand: main + amount: 1 + item: {0}soil_surveyor + actions: + # If the fertilizer doesn't exist, this line would not appear + conditional_fertilizer_action: + type: conditional + value: + conditions: + requirement_1: + type: fertilizer + value: + has: true + actions: + fertilizer_hologram: + type: hologram + value: + duration: 20 + text: '{icon} {left_times}/{max_times} ' + apply-correction: true + x: 0.5 + y: 0.83 + z: 0.5 + # Display the amount of water in pot + water_hologram: + type: hologram + value: + duration: 20 + text: '{water_bar}' + apply-correction: true + x: 0.5 + y: 0.6 + z: 0.5 diff --git a/plugin/src/main/resources/contents/sprinklers/default.yml b/plugin/src/main/resources/contents/sprinklers/default.yml index c458cdc..3fec79c 100644 --- a/plugin/src/main/resources/contents/sprinklers/default.yml +++ b/plugin/src/main/resources/contents/sprinklers/default.yml @@ -1,89 +1,115 @@ -# Sprinkler key name +# ID sprinkler_1: # This decides the work range - # (1+1x2)²=9 - # 洒水器工作范围 3x3 + # □□□ + # □■□ + # □□□ range: 1 - # max water storage - # 最大储水量 - storage: 4 - # The water amount add to the pot - # 单次洒水加到种植盆的水量 + # Maximum water storage capacity + storage: 5 + # Unlimited water source + infinite: false + # Amount of water added in a single sprinkling water: 1 - # 2/3D物品 - 3D-item: customcrops:sprinkler_1 - # Optional, remove this section if you don't need 2d items - 2D-item: customcrops:sprinkler_1_item + # Basic 3d model + 3D-item: {0}sprinkler_1 + # Optional 2d model + 2D-item: {0}sprinkler_1_item # ITEM_FRAME / TRIPWIRE / ITEM_DISPLAY (1.19.4+) - # Sprinkler item type type: ITEM_FRAME - # The sprinkler can only add water to certain pots - # 洒水器只能对指定种植盆浇水 + # Sprinklers only work in specified planting pots pot-whitelist: - default - # see how fill-method works in /CustomCrops/contents/pots/default.yml - # 请在CustomCrops/contents/pots/default.yml查看"fill-method"如何使用 + # Methods to fill the sprinkler fill-method: + # The name of the method, it's totally customized method_1: + # The item to use item: WATER_BUCKET + # The item returned (Optional) return: BUCKET + # The amount of water to add amount: 3 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient method_2: item: WATER return: GLASS_BOTTLE amount: 1 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient - # The sound to play when placing - # 放置时候的声音 - place-sound: minecraft:block.bone_block.place - # The hologram to show when interacting - # 交互时候产生的悬浮信息 - hologram: - enable: true - # ARMOR_STAND / TEXT_DISPLAY (1.19.4+) - type: ARMOR_STAND - # vertical offset(change this according to your model size) - # 竖直方向的偏移 - vertical-offset: -0.1 - duration: 1 - # Available placeholders: - # {water_bar} water bar image - # {current} current water - # {storage} max storage - content: '{water_bar}' - water-bar: - left: '뀂' - full: '뀁뀃' - empty: '뀁뀄' - right: '뀁뀅' - text-display-options: - background-color: 0,0,0,0 - has-shadow: false - is-see-through: false - use-default-background-color: false - text-opacity: -1 - # Water splash animation when sprinkler works - # 洒水器在工作时候的洒水效果 - animation: - enable: true - # in seconds - # 持续时间 - duration: 10 - vertical-offset: 0.4 - item: customcrops:water_effect - # ARMOR_STAND / ITEM_DISPLAY (1.19.4+) - type: ARMOR_STAND - + # Water bar image settings + water-bar: + left: '뀂' + full: '뀁뀃' + empty: '뀁뀄' + right: '뀁뀅' + # Event settings + events: + place: + swing_hand_action: + type: swing-hand + value: true + sound_action: + type: sound + value: + source: player + key: minecraft:block.bone_block.place + volume: 1 + pitch: 1 + interact: + force_work_action: + type: conditional + value: + conditions: + requirement_1: + type: sneak + value: true + actions: + action_1: + type: force-tick + hologram_action: + type: hologram + value: + text: '{water_bar}' + duration: 40 + x: 0.5 + y: -0.3 + z: 0.5 + visible-to-all: false + work: + fake_item_action: + type: fake-item + value: + item: customcrops:water_effect + duration: 100 + x: 0.5 + y: 0.4 + z: 0.5 + visible-to-all: true + add_water: + particle_action: + type: particle + value: + particle: WATER_SPLASH + x: 0.5 + y: 0.7 + z: 0.5 + count: 5 + sound_action: + type: sound + value: + key: "minecraft:item.bucket.empty" + source: 'player' + volume: 1 + pitch: 1 sprinkler_2: - # (1+2x2)²=25 + # □□□□□ + # □□□□□ + # □□■□□ + # □□□□□ + # □□□□□ range: 2 storage: 5 water: 1 - 3D-item: customcrops:sprinkler_2 - 2D-item: customcrops:sprinkler_2_item + 3D-item: {0}sprinkler_2 + 2D-item: {0}sprinkler_2_item type: ITEM_FRAME pot-whitelist: - default @@ -92,45 +118,86 @@ sprinkler_2: item: WATER_BUCKET return: BUCKET amount: 3 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient method_2: item: WATER return: GLASS_BOTTLE amount: 1 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient - place-sound: minecraft:block.bone_block.place - hologram: - enable: true - type: ARMOR_STAND - vertical-offset: -0.1 - duration: 1 - content: '{water_bar}' - water-bar: - left: '뀂' - full: '뀁뀃' - empty: '뀁뀄' - right: '뀁뀅' - text-display-options: - background-color: 0,0,0,0 - has-shadow: false - is-see-through: false - use-default-background-color: false - text-opacity: -1 - animation: - enable: true - duration: 10 - vertical-offset: 0.4 - item: customcrops:water_effect - type: ARMOR_STAND - + water-bar: + left: '뀂' + full: '뀁뀃' + empty: '뀁뀄' + right: '뀁뀅' + events: + place: + swing_hand_action: + type: swing-hand + value: true + sound_action: + type: sound + value: + source: player + key: minecraft:block.bone_block.place + volume: 1 + pitch: 1 + interact: + force_work_action: + type: conditional + value: + conditions: + requirement_1: + type: sneak + value: true + actions: + action_1: + type: force-tick + hologram_action: + type: hologram + value: + text: '{water_bar}' + duration: 40 + x: 0.5 + y: -0.3 + z: 0.5 + visible-to-all: false + work: + fake_item_action: + type: fake-item + value: + item: customcrops:water_effect + duration: 100 + x: 0.5 + y: 0.4 + z: 0.5 + visible-to-all: true + add_water: + particle_action: + type: particle + value: + particle: WATER_SPLASH + x: 0.5 + y: 0.7 + z: 0.5 + count: 5 + sound_action: + type: sound + value: + key: "minecraft:item.bucket.empty" + source: 'player' + volume: 1 + pitch: 1 sprinkler_3: - range: 2 + # □□□□□□□ + # □□□□□□□ + # □□□□□□□ + # □□□■□□□ + # □□□□□□□ + # □□□□□□□ + # □□□□□□□ + range: 3 storage: 3 - water: 2 - 3D-item: customcrops:sprinkler_3 - 2D-item: customcrops:sprinkler_3_item + water: 1 + 3D-item: {0}sprinkler_3 + 2D-item: {0}sprinkler_3_item type: ITEM_FRAME pot-whitelist: - default @@ -139,35 +206,70 @@ sprinkler_3: item: WATER_BUCKET return: BUCKET amount: 3 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient method_2: item: WATER return: GLASS_BOTTLE amount: 1 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient - place-sound: minecraft:block.bone_block.place - hologram: - enable: true - type: ARMOR_STAND - vertical-offset: -0.1 - duration: 1 - content: '{water_bar}' - water-bar: - left: '뀂' - full: '뀁뀃' - empty: '뀁뀄' - right: '뀁뀅' - text-display-options: - background-color: 0,0,0,0 - has-shadow: false - is-see-through: false - use-default-background-color: false - text-opacity: -1 - animation: - enable: true - duration: 10 - vertical-offset: 0.4 - item: customcrops:water_effect - type: ARMOR_STAND \ No newline at end of file + water-bar: + left: '뀂' + full: '뀁뀃' + empty: '뀁뀄' + right: '뀁뀅' + events: + place: + swing_hand_action: + type: swing-hand + value: true + sound_action: + type: sound + value: + source: player + key: minecraft:block.bone_block.place + volume: 1 + pitch: 1 + interact: + force_work_action: + type: conditional + value: + conditions: + requirement_1: + type: sneak + value: true + actions: + action_1: + type: force-tick + hologram_action: + type: hologram + value: + text: '{water_bar}' + duration: 40 + x: 0.5 + y: -0.3 + z: 0.5 + visible-to-all: false + work: + fake_item_action: + type: fake-item + value: + item: customcrops:water_effect + duration: 100 + x: 0.5 + y: 0.4 + z: 0.5 + visible-to-all: true + add_water: + particle_action: + type: particle + value: + particle: WATER_SPLASH + x: 0.5 + y: 0.7 + z: 0.5 + count: 5 + sound_action: + type: sound + value: + key: "minecraft:item.bucket.empty" + source: 'player' + volume: 1 + pitch: 1 \ No newline at end of file diff --git a/plugin/src/main/resources/contents/utils/default.yml b/plugin/src/main/resources/contents/utils/default.yml new file mode 100644 index 0000000..e69de29 diff --git a/plugin/src/main/resources/contents/watering-cans/default.yml b/plugin/src/main/resources/contents/watering-cans/default.yml index f536974..48ac452 100644 --- a/plugin/src/main/resources/contents/watering-cans/default.yml +++ b/plugin/src/main/resources/contents/watering-cans/default.yml @@ -1,116 +1,113 @@ -# Watering can key name +# ID watering_can_1: - # 物品 id - item: customcrops:watering_can_1 - # This is optional. It would decide the look of the watering can when having different water amount - # 可选配置,他会影响水壶在不同水量情况下的外观 + # ItemsAdder/Oraxen item ID + item: {0}watering_can_1 + # This is optional. It will determine the appearance of watering cans with different water storage capacities #appearance: - # # custom-model-data - # 0: 1000 + # 0: 1000 # CustomModelData # 1: 1001 # 2: 1002 # 3: 1003 - # 储水量 + # Maximum water storage capacity capacity: 3 + # Amount of water added to pot in a single interaction + water: 1 # Effective Range - # 有效范围 effective-range: width: 1 length: 1 - # 粒子效果 - particle: WATER_SPLASH - # 声音 - sound: minecraft:block.water.ambient # Methods to fill the watering can fill-method: + # The name of the method, it's totally customized method_1: - # The target block/furniture id - # use capital letters for vanilla items + # The target block/furniture (That means you can create a well to get water) target: WATER - # water amount to add - # 加水量 + # Amount of water to add amount: 1 - particle: WATER_SPLASH - sound: minecraft:item.bucket.fill - # This decides where the watering can can be used - # 这决定了水壶能使用在哪些种植盆上 + # Watering cans only work in specified planting pots pot-whitelist: - default - # allows you to add water to sprinkler by clicking with a watering can - # 允许水壶对洒水器加水 + # Allow watering can to divert water to sprinkler sprinkler-whitelist: - sprinkler_1 - sprinkler_2 - sprinkler_3 + # Dynamic lore settings dynamic-lore: # Should watering-can has dynamic lore according to the water amount - # 水壶是否有根据水量变化的动态lore enable: true # Available placeholders: - # {water_bar} water bar image - # {current} current water - # {storage} max storage + # {water_bar}: water bar image + # {current}: current amount of water + # {storage}: max water storage lore: - '{water_bar}' - 'Right click water to add water to the can.' - actionbar: - # Should actionbar be sent when using watering-can - # 是否发送动作栏信息 - enable: true - # Available placeholders {current} {storage} {water_bar} - content: '{water_bar}' + # Water bar image settings water-bar: left: '뀂' full: '뀁뀃' empty: '뀁뀄' right: '뀁뀅' - + # Event settings + events: + # No water + no_water: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] Water has been used out' + # Consume water + consume_water: + # The name of the action, it's totally customized + sound_action: + type: sound + value: + key: "minecraft:block.water.ambient" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '{water_bar}' + # Add water to the watering-can + add_water: + # The name of the action, it's totally customized + particle_action: + type: particle + value: + particle: WATER_SPLASH + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + sound_action: + type: sound + value: + key: "minecraft:item.bucket.empty" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '{water_bar}' watering_can_2: - item: customcrops:watering_can_2 + item: {0}watering_can_2 capacity: 4 - effective-range: - width: 1 - length: 2 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient - fill-method: - method_1: - target: WATER - amount: 1 - particle: WATER_SPLASH - pot-whitelist: - - default - sprinkler-whitelist: - - sprinkler_1 - - sprinkler_2 - - sprinkler_3 - dynamic-lore: - enable: true - lore: - - '{water_bar}' - - 'Right click water to add water to the can.' - actionbar: - enable: true - content: '{water_bar}' - water-bar: - left: '뀂' - full: '뀁뀃' - empty: '뀁뀄' - right: '뀁뀅' - -watering_can_3: - item: customcrops:watering_can_3 - capacity: 5 + water: 1 effective-range: width: 1 length: 3 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient fill-method: method_1: target: WATER amount: 1 - particle: WATER_SPLASH pot-whitelist: - default sprinkler-whitelist: @@ -122,28 +119,133 @@ watering_can_3: lore: - '{water_bar}' - 'Right click water to add water to the can.' - actionbar: - enable: true - content: '{water_bar}' water-bar: left: '뀂' full: '뀁뀃' empty: '뀁뀄' right: '뀁뀅' - + events: + no_water: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] Water has been used out' + consume_water: + sound_action: + type: sound + value: + key: "minecraft:block.water.ambient" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '{water_bar}' + add_water: + particle_action: + type: particle + value: + particle: WATER_SPLASH + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + sound_action: + type: sound + value: + key: "minecraft:item.bucket.empty" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '{water_bar}' +watering_can_3: + item: {0}watering_can_3 + capacity: 5 + water: 1 + effective-range: + width: 2 + length: 2 + fill-method: + method_1: + target: WATER + amount: 1 + pot-whitelist: + - default + sprinkler-whitelist: + - sprinkler_1 + - sprinkler_2 + - sprinkler_3 + dynamic-lore: + enable: true + lore: + - '{water_bar}' + - 'Right click water to add water to the can.' + water-bar: + left: '뀂' + full: '뀁뀃' + empty: '뀁뀄' + right: '뀁뀅' + events: + no_water: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] Water has been used out' + consume_water: + sound_action: + type: sound + value: + key: "minecraft:block.water.ambient" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '{water_bar}' + add_water: + particle_action: + type: particle + value: + particle: WATER_SPLASH + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + sound_action: + type: sound + value: + key: "minecraft:item.bucket.empty" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '{water_bar}' watering_can_4: - item: customcrops:watering_can_4 + item: {0}watering_can_4 capacity: 3 + water: 1 effective-range: width: 3 - length: 3 - particle: WATER_SPLASH - sound: minecraft:block.water.ambient + length: 2 fill-method: method_1: target: WATER amount: 1 - particle: WATER_SPLASH pot-whitelist: - default sprinkler-whitelist: @@ -155,11 +257,50 @@ watering_can_4: lore: - '{water_bar}' - 'Right click water to add water to the can.' - actionbar: - enable: true - content: '{water_bar}' water-bar: left: '뀂' full: '뀁뀃' empty: '뀁뀄' right: '뀁뀅' + events: + no_water: + sound_action: + type: sound + value: + key: "minecraft:item.bundle.insert" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '[X] Water has been used out' + consume_water: + sound_action: + type: sound + value: + key: "minecraft:block.water.ambient" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '{water_bar}' + add_water: + particle_action: + type: particle + value: + particle: WATER_SPLASH + x: 0.5 + y: 1.3 + z: 0.5 + count: 5 + sound_action: + type: sound + value: + key: "minecraft:item.bucket.empty" + source: 'player' + volume: 1 + pitch: 1 + actionbar_action: + type: actionbar + value: '{water_bar}' \ No newline at end of file diff --git a/plugin/src/main/resources/messages/english.yml b/plugin/src/main/resources/messages/english.yml new file mode 100644 index 0000000..6f4ce57 --- /dev/null +++ b/plugin/src/main/resources/messages/english.yml @@ -0,0 +1,8 @@ +messages: + prefix: '[CustomCrops] ' + reload: 'Reloaded! Took {time}ms.' + spring: 'Spring' + summer: 'Summer' + autumn: 'Autumn' + winter: 'Winter' + no-season: 'Season Disabled' \ No newline at end of file diff --git a/plugin/src/main/resources/messages/messages_chinese.yml b/plugin/src/main/resources/messages/messages_chinese.yml deleted file mode 100644 index f6f2350..0000000 --- a/plugin/src/main/resources/messages/messages_chinese.yml +++ /dev/null @@ -1,25 +0,0 @@ -# https://docs.advntr.dev/minimessage/format.html -messages: - prefix: '[CustomCrops] ' - reload: '已重载! 用时 {time}ms.' - invalid-args: '无效参数.' - no-console: '这个命令只能由玩家使用.' - not-online: '玩家 {player} 当前不在线.' - lack-args: '缺少参数.' - not-none-args: '你需要输入一个参数.' - before-plant: '这种肥料必须在种植之前使用.' - unsuitable-pot: "你不能在这里面种植它." - reach-crop-limit: '农作物数量已达上限.' - no-perm: "你没有权限这样做." - spring: '春季' - summer: '夏季' - autumn: '秋季' - winter: '冬季' - no-season: '这个世界禁用了季节' - set-season: "成功将 {world} 世界的季节设置为 {season}." - set-date: "成功将 {world} 世界的日期设置为 {date}." - world-not-exist: '世界 {world} 不存在.' - season-not-exist: '{season} 不是一个有效的季节.' - force-sprinkler-work: '已强制世界 {world} 的洒水器进行工作.' - force-consume: '已强制世界 {world} 的肥料、水分消耗.' - force-grow: '已强制世界 {world} 的农作物生长.' \ No newline at end of file diff --git a/plugin/src/main/resources/messages/messages_english.yml b/plugin/src/main/resources/messages/messages_english.yml deleted file mode 100644 index 7a5e6ab..0000000 --- a/plugin/src/main/resources/messages/messages_english.yml +++ /dev/null @@ -1,25 +0,0 @@ -# https://docs.advntr.dev/minimessage/format.html -messages: - prefix: '[CustomCrops] ' - reload: 'Reloaded! Took {time}ms.' - invalid-args: 'Invalid arguments.' - no-console: 'This command can only be executed by a player.' - not-online: 'Player {player} is not online.' - lack-args: 'Arguments are insufficient.' - not-none-args: 'Not a none argument command.' - before-plant: 'This fertilizer should be used before planting.' - unsuitable-pot: "You can't plant the seed in this pot." - reach-crop-limit: 'The number of crops has reached the limitation.' - no-perm: "You don't have permission to do that." - spring: 'Spring' - summer: 'Summer' - autumn: 'Autumn' - winter: 'Winter' - no-season: 'SEASON DISABLED IN THIS WORLD' - set-season: "Successfully set {world}'s season to {season}." - set-date: "Successfully set {world}'s date to {date}." - world-not-exist: 'World {world} does not exist.' - season-not-exist: 'Season {season} does not exist.' - force-sprinkler-work: "Forced {world}'s sprinklers to start working." - force-consume: "Forced {world}'s pots to reduce water amount and the remaining use of fertilizers." - force-grow: "Forced {world}'s crops to grow one point." \ No newline at end of file diff --git a/plugin/src/main/resources/messages/messages_french.yml b/plugin/src/main/resources/messages/messages_french.yml deleted file mode 100644 index b214b4b..0000000 --- a/plugin/src/main/resources/messages/messages_french.yml +++ /dev/null @@ -1,25 +0,0 @@ -# https://docs.advntr.dev/minimessage/format.html -messages: - prefix: '[CustomCrops] ' - reload: 'Rechargé ! A pris {time}ms.' - invalid-args: 'Arguments invalides.' - no-console: 'Cette commande ne peut être exécutée que par un joueur.' - not-online: 'Joueur {player} n'est pas en ligne.' - lack-args: 'Les arguments sont insuffisants.' - not-none-args: 'Commande avec argument invalide.' - before-plant: 'Cet engrais doit être utilisé avant la plantation.' - unsuitable-pot: "Vous ne pouvez pas planter la graine dans ce pot." - reach-crop-limit: 'Le nombre de cultures a atteint la limite.' - no-perm: "Vous n'avez pas la permission de faire cela." - spring: 'Printemps' - summer: 'Été' - autumn: 'Automne' - winter: 'Hiver' - no-season: 'SAISON DÉSACTIVÉE DANS CE MONDE' - set-season: "Réglage réussi de la saison de {world} à {season}." - set-date: "Réglage réussi de la date de {world} à {date}." - world-not-exist: 'Le monde {world} n\'existe pas.' - season-not-exist: 'La saison {season} n\'existe pas.' - force-sprinkler-work: "Forçage des arroseurs du monde {world} à commencer à fonctionner." - force-consume: "Forçage des pots du monde {world} à réduire la quantité d'eau et l'utilisation restante d'engrais." - force-grow: "Forçage des cultures du monde {world} à pousser d'un point." diff --git a/plugin/src/main/resources/messages/messages_russian.yml b/plugin/src/main/resources/messages/messages_russian.yml deleted file mode 100644 index 8a16f0f..0000000 --- a/plugin/src/main/resources/messages/messages_russian.yml +++ /dev/null @@ -1,24 +0,0 @@ -messages: - prefix: '[CustomCrops] ' - reload: 'Перезагружено! Заняло {time}мс.' - invalid-args: 'Ошибочные аргументы.' - no-console: 'Эта команда может быть выполнена только игроком.' - not-online: 'Игрок {player} не в сети.' - lack-args: 'Недостаточно аргументов.' - not-none-args: 'В команде обязательны аргументы.' - before-plant: 'Это удобрение нужно использовать перед посадкой.' - unsuitable-pot: "Вы не сможете посадить семечко в этот горшок." - reach-crop-limit: 'Количество посевов достигло лимита.' - no-perm: "У вас нет прав на это." - spring: 'Весна' - summer: 'Лето' - autumn: 'Осень' - winter: 'Зима' - no-season: 'СЕЗОНЫ ОТКЛЮЧЕНЫ В ЭТОМ МИРЕ' - set-season: "Успешно установлен сезон {season} в {world}." - set-date: "Успешно установлена дата {date} в {world}." - world-not-exist: 'Мир {world} не существует.' - season-not-exist: 'Сезон {season} не существует.' - force-sprinkler-work: "Принудительно в {world} запущены спринклеры." - force-consume: "Принудительно в {world} в горшках потратились вода и удобрение." - force-grow: "Принудительно в {world} посевы выросли на одну стадию." diff --git a/plugin/src/main/resources/messages/messages_spanish.yml b/plugin/src/main/resources/messages/messages_spanish.yml deleted file mode 100644 index c719cd4..0000000 --- a/plugin/src/main/resources/messages/messages_spanish.yml +++ /dev/null @@ -1,25 +0,0 @@ -# https://docs.advntr.dev/minimessage/format.html -messages: - prefix: '[CustomCrops] ' - reload: '¡Recargado! Tomó {time}ms.' - invalid-args: 'Argumentos invalidos.' - no-console: 'Este comando solo puede ser ejecutado por un jugador' - not-online: 'El jugador {player} no esta conectado' - lack-args: 'Los argumentos son insuficientes' - not-none-args: 'Este comando no tiene argumentos' - before-plant: 'Este fertilizador debe ser usado antes de plantarlo' - unsuitable-pot: "No puedes plantar esta semilla en este pot" - reach-crop-limit: 'El numero de crops ha llegado al limite' - no-perm: "No tienes permiso para esto" - spring: 'Primavera' - summer: 'Verano' - autumn: 'Otoño' - winter: 'Invierno' - no-season: 'LAS TEMPORADAS ESTAN DESACTIVADAS EN ESTE MUNDO' - set-season: "Correctamente establecido {world}'s temporada a {season}." - set-date: "Correctamente establecido {world}'s fecha para {date}." - world-not-exist: 'El mundo {world} no existe.' - season-not-exist: 'Temporada {season} no existe.' - force-sprinkler-work: "Se forzo {world}'s sprinklers para trabajar" - force-consume: "Se forzo {world}'s pot para reducir la cantidad de consumo del agua y fertilizantes" - force-grow: "Forced {world}'s crops to grow one point." \ No newline at end of file diff --git a/plugin/src/main/resources/messages/messages_turkish.yml b/plugin/src/main/resources/messages/messages_turkish.yml deleted file mode 100644 index 672dd60..0000000 --- a/plugin/src/main/resources/messages/messages_turkish.yml +++ /dev/null @@ -1,26 +0,0 @@ -# https://docs.advntr.dev/minimessage/format.html -messages: - prefix: '[CustomCrops] ' - reload: 'Yenilendi! {time}ms sürdü.' - invalid-args: 'Geçersiz argümanlar. (argüman örneği: /customcrops )' - no-console: 'Bu komut sadece bir oyuncu tarafından kullanılabilir.' - not-online: '{player} adındaki oyuncu aktif değil.' - lack-args: 'Yetersiz argümanlar. Geçersiz argümanlar. (argüman örneği: /customcrops )' - not-none-args: 'Bu komut için bir argüman belirtmelisin. (argüman örneği: /customcrops )' - before-plant: 'Bu gübre, ekimden önce kullanılmalıdır!' - unsuitable-pot: "Tohumu bu saksıya ekemezsin." - reach-crop-limit: 'Ekinlerin sayısı sınıra ulaştı.' - no-perm: "Bunu yapmak için yetkin yok." - spring: 'Bahar' - summer: 'Yaz' - autumn: 'Sonbahar' - winter: 'Kış' - no-season: 'MEVSİMLER BU DÜNYADA DEVRE DIŞI BIRAKILDI' - set-season: "{world} adlı dünyanın mevsimi başarıyla {season} ile değiştirildi." - set-date: "{world} adlı dünyanın tarihi başarıyla {date} ile değiştirildi." - world-not-exist: '{world} adında bir dünya bulunamadı.' - season-not-exist: '{season} adında bir mevsim bulunamadı.' - force-sprinkler-work: "{world} adındaki dünyadaki sulayıcılar çalışmaya zorlandı." - force-consume: "{world} adındaki dünyadaki saksıların su miktarının ve gübrelerin kalan kullanımın azaltılması zorlandı." - # Not sure about force-grow. Does that mean like growing in one direction? - force-grow: "{world} adındaki dünyadaki ekinlerin bir yöne doğru büyümesi zorlandı." diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 647d4e9..a3c51a8 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: CustomCrops version: '${version}' -main: net.momirealms.customcrops.CustomCrops +main: net.momirealms.customcrops.CustomCropsPluginImpl api-version: 1.17 load: POSTWORLD authors: [ XiaoMoMi ] @@ -11,62 +11,15 @@ softdepend: - ItemsAdder - Oraxen - PlaceholderAPI - - mcMMO - - AureliumSkills - - MMOCore - - EcoSkills - - RealisticSeasons - - AdvancedSeasons - - Jobs - - IridiumSkyblock - - SuperiorSkyBlock2 - - Residence - - Kingdoms - - WorldGuard - - GriefDefender - - PlotSquared - - Towny - - Lands - - GriefPrevention - - CrashClaim - - BentoBox - EcoJobs - BattlePass - BetonQuest - ClueScrolls - - HuskTowns -commands: - customcrops: - usage: /customcrops - permission: customcrops.admin - aliases: - - ccrops -permissions : - customcrops.*: - default: op - children: - customcrops.admin: true - customcrops.reload: true - customcrops.help: true - customcrops.about: true - customcrops.setdate: true - customcrops.setseason: true - customcrops.force: true - customcrops.migrate: true - customcrops.convert: true - customcrops.reload: - default: op - customcrops.help: - default: op - customcrops.about: - default: op - customcrops.setdate: - default: op - customcrops.setseason: - default: op - customcrops.force: - default: op - customcrops.migrate: - default: op - customcrops.convert: - default: op \ No newline at end of file + - mcMMO + - AureliumSkills + - AuraSkills + - MMOCore + - EcoSkills + - Jobs + - RealisticSeasons + - AdvancedSeasons \ No newline at end of file