From 6cd818cce36e33f6df7d6eebacdea7eb0e52b3d3 Mon Sep 17 00:00:00 2001 From: SoKnight Date: Sat, 18 Jul 2020 13:18:07 +0500 Subject: [PATCH] 2.0.3 - Some fixes and improvements --- .gitignore | 131 ++++ LICENSE | 674 ++++++++++++++++++ README.md | 9 + pom.xml | 154 ++++ .../java/ru/soknight/peconomy/PEcoAPI.java | 175 +++++ .../java/ru/soknight/peconomy/PEconomy.java | 152 ++++ .../peconomy/command/CommandBalance.java | 175 +++++ .../soknight/peconomy/command/CommandPay.java | 216 ++++++ .../peconomy/command/SubcommandHandler.java | 35 + .../peconomy/command/sub/CommandAdd.java | 168 +++++ .../peconomy/command/sub/CommandHelp.java | 61 ++ .../peconomy/command/sub/CommandHistory.java | 207 ++++++ .../peconomy/command/sub/CommandInfo.java | 160 +++++ .../peconomy/command/sub/CommandReload.java | 39 + .../peconomy/command/sub/CommandReset.java | 150 ++++ .../peconomy/command/sub/CommandSet.java | 171 +++++ .../peconomy/command/sub/CommandTake.java | 166 +++++ .../command/tool/AmountFormatter.java | 11 + .../command/tool/SourceFormatter.java | 22 + .../command/validation/AmountValidator.java | 42 ++ .../command/validation/CurrencyValidator.java | 38 + .../validation/WalletExecutionData.java | 33 + .../command/validation/WalletValidator.java | 38 + .../configuration/CurrenciesManager.java | 93 +++ .../configuration/CurrencyInstance.java | 22 + .../configuration/MessagesProvider.java | 59 ++ .../soknight/peconomy/database/Database.java | 67 ++ .../peconomy/database/DatabaseManager.java | 144 ++++ .../peconomy/database/Transaction.java | 65 ++ .../peconomy/database/TransactionType.java | 7 + .../ru/soknight/peconomy/database/Wallet.java | 61 ++ .../soknight/peconomy/hook/PEcoExpansion.java | 70 ++ .../soknight/peconomy/hook/VaultEconomy.java | 370 ++++++++++ .../peconomy/listener/PlayerJoinListener.java | 54 ++ src/main/resources/config.yml | 95 +++ src/main/resources/currencies.yml | 46 ++ src/main/resources/locales/messages_en.yml | 164 +++++ src/main/resources/locales/messages_ru.yml | 162 +++++ src/main/resources/plugin.yml | 107 +++ 39 files changed, 4613 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/ru/soknight/peconomy/PEcoAPI.java create mode 100644 src/main/java/ru/soknight/peconomy/PEconomy.java create mode 100644 src/main/java/ru/soknight/peconomy/command/CommandBalance.java create mode 100644 src/main/java/ru/soknight/peconomy/command/CommandPay.java create mode 100644 src/main/java/ru/soknight/peconomy/command/SubcommandHandler.java create mode 100644 src/main/java/ru/soknight/peconomy/command/sub/CommandAdd.java create mode 100644 src/main/java/ru/soknight/peconomy/command/sub/CommandHelp.java create mode 100644 src/main/java/ru/soknight/peconomy/command/sub/CommandHistory.java create mode 100644 src/main/java/ru/soknight/peconomy/command/sub/CommandInfo.java create mode 100644 src/main/java/ru/soknight/peconomy/command/sub/CommandReload.java create mode 100644 src/main/java/ru/soknight/peconomy/command/sub/CommandReset.java create mode 100644 src/main/java/ru/soknight/peconomy/command/sub/CommandSet.java create mode 100644 src/main/java/ru/soknight/peconomy/command/sub/CommandTake.java create mode 100644 src/main/java/ru/soknight/peconomy/command/tool/AmountFormatter.java create mode 100644 src/main/java/ru/soknight/peconomy/command/tool/SourceFormatter.java create mode 100644 src/main/java/ru/soknight/peconomy/command/validation/AmountValidator.java create mode 100644 src/main/java/ru/soknight/peconomy/command/validation/CurrencyValidator.java create mode 100644 src/main/java/ru/soknight/peconomy/command/validation/WalletExecutionData.java create mode 100644 src/main/java/ru/soknight/peconomy/command/validation/WalletValidator.java create mode 100644 src/main/java/ru/soknight/peconomy/configuration/CurrenciesManager.java create mode 100644 src/main/java/ru/soknight/peconomy/configuration/CurrencyInstance.java create mode 100644 src/main/java/ru/soknight/peconomy/configuration/MessagesProvider.java create mode 100644 src/main/java/ru/soknight/peconomy/database/Database.java create mode 100644 src/main/java/ru/soknight/peconomy/database/DatabaseManager.java create mode 100644 src/main/java/ru/soknight/peconomy/database/Transaction.java create mode 100644 src/main/java/ru/soknight/peconomy/database/TransactionType.java create mode 100644 src/main/java/ru/soknight/peconomy/database/Wallet.java create mode 100644 src/main/java/ru/soknight/peconomy/hook/PEcoExpansion.java create mode 100644 src/main/java/ru/soknight/peconomy/hook/VaultEconomy.java create mode 100644 src/main/java/ru/soknight/peconomy/listener/PlayerJoinListener.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/currencies.yml create mode 100644 src/main/resources/locales/messages_en.yml create mode 100644 src/main/resources/locales/messages_ru.yml create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05cf900 --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ +# Created by https://www.gitignore.io/api/java,linux,maven,eclipse +# Edit at https://www.gitignore.io/?templates=java,linux,maven,eclipse +# +# Thanks to Voidpointer (https://github.com/NyanGuyMF) for this .gitignore file + +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# ignore bash scripts +*.sh + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +### Eclipse Patch ### +# Eclipse Core +.project + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Annotation Processing +.apt_generated + +.sts4-cache/ + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# End of https://www.gitignore.io/api/java,linux,maven,eclipse \ No newline at end of file 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 new file mode 100644 index 0000000..b8524d2 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# PEconomy +Minecraft economy plugin with dollar and euro wallets. + +May work on MC version 1.13-1.15 (Paper or Spigot), but tested only on 1.15.2. +You can operate with two currencies by default: dollar and euro (second wallet may be donation for example). +Sure, you can add own currencies or remove defaults, easy :) +This plugin hooks into Vault as economy provider and uses [SKLibrary](https://github.com/SoKnight/SKLibrary) as dependency. + +For more information see [wiki](https://github.com/SoKnight/PEconomy/wiki). diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..91ef2f2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,154 @@ + + 4.0.0 + ru.soknight + peconomy + 2.0.3 + + PEconomy + Server economy provider with unlimited wallets and Vault support + + + 1.8 + 1.8 + UTF-8 + github + + + + + internal.repo + Temporary staging repository + file://${project.build.directory}/mvn-repo + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + placeholderapi + https://repo.extendedclip.com/content/repositories/placeholderapi/ + + + jitpack.io + https://jitpack.io + + + SKLibrary-mvn-repo + https://raw.github.com/SoKnight/SKLibrary/tree/mvn-repo/ + + + + + + + org.spigotmc + spigot-api + 1.13.2-R0.1-SNAPSHOT + provided + + + + me.clip + placeholderapi + 2.10.5 + provided + + + + org.projectlombok + lombok + 1.18.12 + provided + + + + ru.soknight + sklibrary + 1.9.0 + provided + + + + com.github.MilkBowl + VaultAPI + 1.7 + provided + + + + + + + src/main/resources + true + + + + + + maven-deploy-plugin + 2.8.1 + + ${project.groupId}.${project.artifactId}::default::file://${project.build.directory}/mvn-repo + + + + + com.github.github + site-maven-plugin + 0.12 + + Maven artifacts for ${project.version} + true + ${project.build.directory}/mvn-repo + refs/heads/mvn-repo + **/* + PEconomy + SoKnight + + + + + site + + deploy + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + false + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/src/main/java/ru/soknight/peconomy/PEcoAPI.java b/src/main/java/ru/soknight/peconomy/PEcoAPI.java new file mode 100644 index 0000000..fe26e1f --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/PEcoAPI.java @@ -0,0 +1,175 @@ +package ru.soknight.peconomy; + +import lombok.AllArgsConstructor; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.Wallet; + +@AllArgsConstructor +public class PEcoAPI { + + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + /** + * Gets total count of wallets in the database + * @return Total count of wallets + */ + public int getWalletsCount() { + return databaseManager.getWalletsCount(); + } + + /** + * Checks if specified player is {@link Wallet} owner + * @param player - Name of player to check + * @return True if player has wallet or false if not + */ + public boolean hasWallet(String player) { + return databaseManager.hasWallet(player); + } + + /** + * Gets player's wallet from the database if it's exists + * @param player - The potential owner of wallet + * @return Exist player's wallet or null if it's not exist + */ + public Wallet getWallet(String player) { + return databaseManager.getWallet(player); + } + + /** + * Updates player's wallet in the database + * @param wallet - Player's wallet which will be updated in the database + */ + public void updateWallet(Wallet wallet) { + if(databaseManager.hasWallet(wallet.getOwner())) + databaseManager.updateWallet(wallet); + else databaseManager.createWallet(wallet); + } + + /** + * Adds amount of specified currency to player's balance + * @param player - Name of target player + * @param currency - Target currency's ID + * @param amount - Amount of currency to add + * @return + * @return Player's {@link Wallet} after transaction (may be null) + */ + public Wallet addAmount(String player, String currency, float amount) { + Wallet wallet = getWallet(player); + + if(wallet == null) return null; + + wallet.addAmount(currency, amount); + return wallet; + } + + /** + * Gets amount of specified currency on the player's balance + * @param player - Name of target player + * @param currency - Target currency's ID + * @return Amount of specified currency on the balance + */ + public float getAmount(String player, String currency) { + Wallet wallet = getWallet(player); + + return wallet == null ? 0F : wallet.getAmount(currency); + } + + /** + * Checks if specified amount is on player's balance + * @param player - Name of player to check + * @param currency - Target currency's ID + * @param amount - Amount of currency to check + * @return True if player has this amount on him balance or false if not + */ + public boolean hasAmount(String player, String currency, float amount) { + Wallet wallet = getWallet(player); + + return wallet == null ? false : wallet.hasAmount(currency, amount); + } + + /** + * Sets specified amount of currency on the player's balance + * @param player - Name of target player + * @param currency - Target currency's ID + * @param amount - New amount of this currency + * @return Player's {@link Wallet} after transaction (may be null) + */ + public Wallet setAmount(String player, String currency, float amount) { + Wallet wallet = getWallet(player); + if(wallet == null) return null; + + wallet.addAmount(currency, amount); + return wallet; + } + + /** + * Nullifies currency's balance in the player's wallet + * @param player - Name of target player + * @param currency - Target currency's ID + * @return Player's {@link Wallet} after transaction (may be null) + */ + public Wallet resetAmount(String player, String currency) { + Wallet wallet = getWallet(player); + if(wallet == null) return null; + + wallet.resetWallet(currency); + return wallet; + } + + /** + * Taking specified amount of currency from player's balance + * @param player - Name of target player + * @param currency - Target currency's ID + * @param amount - Amount of currency to take + * @return Player's {@link Wallet} after transaction (may be null) + */ + public Wallet takeAmount(String player, String currency, float amount) { + Wallet wallet = getWallet(player); + if(wallet == null) return null; + + wallet.takeAmount(currency, amount); + return wallet; + } + + /** + * Gets economy transaction by ID if it has been completed + * @param id - Transaction's ID + * @return Exist transaction object or null if transaction with this ID cannot be found + */ + public Transaction getTransaction(int id) { + return databaseManager.getTransactionByID(id); + } + + /** + * Saves transaction and gets her ID which will be set by database manager + * @param transaction - Transaction to save + * @return Transaction's ID from database (may be null if saving will be failed) + */ + public Integer saveTransaction(Transaction transaction) { + databaseManager.saveTransaction(transaction); + return transaction.getId(); + } + + /** + * Gets currency instance by ID if requested currency has been initialized by PEconomy + * @param id - Target currency's ID + * @return Exist currency instance or null if currency with this ID has not been initialized + */ + public CurrencyInstance getCurrencyByID(String id) { + return currenciesManager.getCurrency(id); + } + + /** + * Checks if currency instance with specified ID is initialized by PEconomy or not + * @param id - Target currency's ID + * @return 'true' if this currency instance initialized or 'false' if not + */ + public boolean isCurrencyInitialized(String id) { + return getCurrencyByID(id) != null; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/PEconomy.java b/src/main/java/ru/soknight/peconomy/PEconomy.java new file mode 100644 index 0000000..99e44aa --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/PEconomy.java @@ -0,0 +1,152 @@ +package ru.soknight.peconomy; + +import org.bukkit.Bukkit; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.ServicePriority; +import org.bukkit.plugin.ServicesManager; +import org.bukkit.plugin.java.JavaPlugin; + +import net.milkbowl.vault.economy.Economy; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.peconomy.command.CommandBalance; +import ru.soknight.peconomy.command.CommandPay; +import ru.soknight.peconomy.command.SubcommandHandler; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.MessagesProvider; +import ru.soknight.peconomy.database.Database; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.hook.PEcoExpansion; +import ru.soknight.peconomy.hook.VaultEconomy; +import ru.soknight.peconomy.listener.PlayerJoinListener; + +public class PEconomy extends JavaPlugin { + + private static PEcoAPI api; + + protected DatabaseManager databaseManager; + protected CurrenciesManager currenciesManager; + + protected Configuration pluginConfig; + protected MessagesProvider messagesProvider; + protected Messages messages; + + @Override + public void onEnable() { + + // Configs initialization + refreshConfigs(); + + // Database initialization + try { + Database database = new Database(this, pluginConfig); + this.databaseManager = new DatabaseManager(this, database); + } catch (Exception e) { + getLogger().severe("Failed to initialize database: " + e.getLocalizedMessage()); + e.printStackTrace(); + Bukkit.getPluginManager().disablePlugin(this); + return; + } + + // Commands executors initialization + registerCommands(); + + // Event listener initialization + registerListeners(); + + // PEconomy API initialization + api = new PEcoAPI(databaseManager, currenciesManager); + + // Bootstrapping hooks + bootstrapHooks(); + + } + + @Override + public void onDisable() { + if(databaseManager != null) + databaseManager.shutdown(); + } + + private void refreshConfigs() { + this.pluginConfig = new Configuration(this, "config.yml"); + this.pluginConfig.refresh(); + + this.messagesProvider = new MessagesProvider(this, pluginConfig); + this.messages = messagesProvider.getMessages(); + + this.currenciesManager = new CurrenciesManager(this, pluginConfig); + } + + private void registerCommands() { + SubcommandHandler subcommandHandler = new SubcommandHandler(this, currenciesManager, databaseManager, pluginConfig, messages); + CommandBalance commandBalance = new CommandBalance(databaseManager, currenciesManager, pluginConfig, messages); + CommandPay commandPay = new CommandPay(databaseManager, currenciesManager, pluginConfig, messages); + + PluginCommand peco = getCommand("peco"); + PluginCommand balance = getCommand("balance"); + PluginCommand pay = getCommand("pay"); + + peco.setExecutor(subcommandHandler); + peco.setTabCompleter(subcommandHandler); + + balance.setExecutor(commandBalance); + balance.setTabCompleter(commandBalance); + + pay.setExecutor(commandPay); + pay.setTabCompleter(commandPay); + } + + private void registerListeners() { + new PlayerJoinListener(this, databaseManager, currenciesManager); + } + + private void bootstrapHooks() { + try { + // PlaceholdersAPI hook + if(pluginConfig.getBoolean("hooks.papi")) { + if(Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + PEcoExpansion papiExpansion = new PEcoExpansion(this, databaseManager); + + if(papiExpansion.register()) + getLogger().info("Hooked into PlaceholdersAPI successfully."); + else getLogger().warning("Hooking into PlaceholdersAPI failed."); + + } else getLogger().info("Couldn't find PlaceholdersAPI to hook into, ignoring it."); + } + + // Vault hook + if(pluginConfig.getBoolean("hooks.vault")) { + if(Bukkit.getPluginManager().getPlugin("Vault") != null) { + Economy economy = new VaultEconomy(databaseManager, currenciesManager, pluginConfig, messages); + + ServicesManager sm = getServer().getServicesManager(); + sm.register(Economy.class, economy, this, ServicePriority.Highest); + + RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); + if(rsp != null) + getLogger().info("Hooked into Vault successfully."); + else getLogger().warning("Hooking into Vault failed."); + + } else getLogger().info("Couldn't find Vault to hook into, ignoring it."); + } + } catch (Exception ignored) {} + } + + public void refresh() { + pluginConfig.refresh(); + messagesProvider.update(pluginConfig); + + registerCommands(); + } + + /** + * Gets initialized API instance or null if PEconomy is not initialized fully + * @return PEconomy API instance (may be null) + */ + public static PEcoAPI getAPI() { + return api; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/CommandBalance.java b/src/main/java/ru/soknight/peconomy/command/CommandBalance.java new file mode 100644 index 0000000..b5cbaee --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/CommandBalance.java @@ -0,0 +1,175 @@ +package ru.soknight.peconomy.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedCommandExecutor; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Wallet; + +public class CommandBalance extends ExtendedCommandExecutor { + + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + private final Configuration config; + private final Messages messages; + + public CommandBalance(DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + super(messages); + + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + this.config = config; + this.messages = messages; + + String permmsg = messages.get("error.no-permissions"); + + Validator permval = new PermissionValidator("peco.command.balance", permmsg); + + super.addValidators(permval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + if(!validateExecution(sender, args)) return; + + String name = sender.getName(); + boolean other = false; + + // Other balance checking execution + if(!args.isEmpty()) { + if(!sender.hasPermission("peco.command.balance.other")) { + messages.getAndSend(sender, "balance.other-balance"); + return; + } + + name = args.get(0); + + OfflinePlayer offline = Bukkit.getOfflinePlayer(name); + if(offline == null) { + messages.sendFormatted(sender, "error.unknown-wallet", "%player%", name); + return; + } + + if(!offline.isOnline()) { + messages.getAndSend(sender, "balance.offline-balance"); + return; + } + + if(!name.equals(sender.getName())) + other = true; + } else { + if(!(sender instanceof Player)) { + messages.getAndSend(sender, "error.only-for-players"); + return; + } + } + + Wallet wallet = databaseManager.getWallet(name); + if(wallet == null) { + messages.sendFormatted(sender, "error.unknown-wallet", "%player%", name); + return; + } + + String format = messages.get("balance.format"); + Map wallets = wallet.getWallets(); + + if(wallets == null || wallets != null && wallets.isEmpty()) { + if(other) + messages.sendFormatted(sender, "balance.empty-other", "%player%", name); + else messages.getAndSend(sender, "balance.empty-self"); + return; + } + + List balances = new ArrayList<>(); + + wallets.forEach((c, a) -> { + CurrencyInstance instance = currenciesManager.getCurrency(c); + String symbol; + + if(instance == null) { + if(config.getBoolean("hide-unknown-currencies")) return; + else symbol = "N/A"; + } else symbol = instance.getSymbol(); + + String amount = AmountFormatter.format(a); + balances.add(messages.format(format, "%amount%", amount, "%currency%", symbol)); + }); + + if(balances.isEmpty()) { + if(other) + messages.sendFormatted(sender, "balance.empty-other", "%player%", name); + else messages.getAndSend(sender, "balance.empty-self"); + } + + String balance; + if(balances.size() == 1) + balance = balances.get(0); + else { + StringBuilder builder = new StringBuilder(balances.get(0)); + String separator = messages.get("balance.separator"); + + for(int i = 1; i < balances.size(); i++) { + builder.append(separator); + builder.append(balances.get(i)); + } + + balance = builder.toString(); + } + + if(other) + messages.sendFormatted(sender, "balance.other", "%player%", name, "%balance%", balance); + else messages.sendFormatted(sender, "balance.self", "%balance%", balance); + } + + @Override + public List executeTabCompletion(CommandSender sender, CommandArguments args) { + if(args.isEmpty() || !validateTabCompletion(sender, args)) return null; + + List output = new ArrayList<>(); + + // Adds other online players if interaction with them is permitted + if(sender.hasPermission("peco.command.balance.other")) { + Collection players = Bukkit.getOnlinePlayers(); + if(!players.isEmpty()) { + String arg = args.get(0).toLowerCase(); + players.parallelStream() + .filter(p -> p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + } + + // Adds other offline players if interaction with them is permitted + if(sender.hasPermission("peco.command.balance.offline")) { + OfflinePlayer[] players = Bukkit.getOfflinePlayers(); + if(players.length != 0) { + String arg = args.get(0).toLowerCase(); + Arrays.stream(players).parallel() + .filter(p -> !p.isOnline() && p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + } + + return output; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/CommandPay.java b/src/main/java/ru/soknight/peconomy/command/CommandPay.java new file mode 100644 index 0000000..fd39e7f --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/CommandPay.java @@ -0,0 +1,216 @@ +package ru.soknight.peconomy.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedCommandExecutor; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.CommandExecutionData; +import ru.soknight.lib.validation.validator.ArgsCountValidator; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.SenderIsPlayerValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.command.tool.SourceFormatter; +import ru.soknight.peconomy.command.validation.AmountValidator; +import ru.soknight.peconomy.command.validation.CurrencyValidator; +import ru.soknight.peconomy.command.validation.WalletExecutionData; +import ru.soknight.peconomy.command.validation.WalletValidator; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.TransactionType; +import ru.soknight.peconomy.database.Wallet; + +public class CommandPay extends ExtendedCommandExecutor { + + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + private final Configuration config; + private final Messages messages; + + public CommandPay(DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + super(messages); + + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + this.config = config; + this.messages = messages; + + String sendermsg = messages.get("error.only-for-players"); + String permmsg = messages.get("error.no-permissions"); + String argsmsg = messages.get("error.wrong-syntax"); + String walletmsg = messages.get("error.unknown-wallet"); + String amountmsg = messages.get("error.arg-is-not-float"); + String currencymsg = messages.get("error.unknown-currency"); + + Validator senderval = new SenderIsPlayerValidator(sendermsg); + Validator permval = new PermissionValidator("peco.command.pay", permmsg); + Validator argsval = new ArgsCountValidator(3, argsmsg); + Validator walletval = new WalletValidator(databaseManager, walletmsg); + Validator amountval = new AmountValidator(amountmsg); + Validator currencyval = new CurrencyValidator(currenciesManager, currencymsg); + + super.addValidators(senderval, permval, argsval, walletval, amountval, currencyval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + String owner = args.get(0), amstr = args.get(1), currencyid = args.get(2); + + CommandExecutionData data = new WalletExecutionData(sender, args, owner, currencyid, amstr); + if(!validateExecution(data)) return; + + String source = sender.getName(); + + if(source.equals(owner)) { + messages.getAndSend(sender, "pay.to-self"); + return; + } + + OfflinePlayer offline = Bukkit.getOfflinePlayer(owner); + if(!offline.isOnline() && sender.hasPermission("peco.command.pay.offline")) { + messages.getAndSend(sender, "pay.offline-pay"); + return; + } + + // Checking receiver wallet exist + Wallet other = databaseManager.getWallet(owner); + if(other == null) { + messages.sendFormatted(sender, "error.unknown-wallet", "%player%", owner); + return; + } + + // Getting some values + float amount = Float.parseFloat(amstr); + Wallet self = databaseManager.getWallet(source); + + float preself = self.getAmount(currencyid); + float postself = preself - amount; + + CurrencyInstance currency = currenciesManager.getCurrency(currencyid); + + // Formatting values + String symbol = currency.getSymbol(); + String preselfstr = AmountFormatter.format(preself); + + // Checking for invalid self balance + String amountstr = AmountFormatter.format(amount); + if(postself < 0) { + messages.sendFormatted(sender, "pay.not-enough", + "%amount%", preselfstr, + "%currency%", symbol, + "%player%", owner, + "%requested%", amountstr); + return; + } + + // Check if limit reached on receiver balance + float preother = other.getAmount(currencyid); + float postother = preother + amount; + float limit = currency.getLimit(); + + if(limit != 0 && postother > limit) { + String limitstr = AmountFormatter.format(limit); + messages.sendFormatted(sender, "pay.limit", + "%currency%", symbol, + "%limit%", limitstr); + return; + } + + // Updating DB + self.takeAmount(currencyid, amount); + other.addAmount(currencyid, amount); + + databaseManager.updateWallet(self); + databaseManager.updateWallet(other); + + // Formatting values + String preotherstr = AmountFormatter.format(preother); + String postotherstr = AmountFormatter.format(postother); + String postselfstr = AmountFormatter.format(postself); + String operationself = messages.get("operation.decrease"); + String operationother = messages.get("operation.increase"); + + // Saving transaction + Transaction transaction = new Transaction(owner, currencyid, TransactionType.PAYMENT, preother, postother, source); + databaseManager.saveTransaction(transaction); + + int id = transaction.getId(); + + // Sending messages to sender and wallet owner if he is online + messages.sendFormatted(sender, "pay.other", + "%amount%", amountstr, + "%currency%", symbol, + "%player%", owner, + "%from%", preselfstr, + "%operation%", operationself, + "%to%", postselfstr, + "%id%", id); + + OfflinePlayer offlinetarget = Bukkit.getOfflinePlayer(owner); + if(offlinetarget.isOnline()) + messages.sendFormatted(offlinetarget.getPlayer(), "pay.self", + "%amount%", amountstr, + "%currency%", symbol, + "%source%", SourceFormatter.format(config, source, offlinetarget.getPlayer()), + "%from%", preotherstr, + "%operation%", operationother, + "%to%", postotherstr, + "%source%", source, + "%id%", id); + } + + @Override + public List executeTabCompletion(CommandSender sender, CommandArguments args) { + if(!validateTabCompletion(sender, args)) return null; + if(!(sender instanceof Player)) return null; + + List output = new ArrayList<>(); + + if(args.size() == 1) { + if(!sender.hasPermission("peco.command.pay.offline")) { + Collection players = Bukkit.getOnlinePlayers(); + String arg = args.get(0).toLowerCase(); + players.parallelStream() + .filter(p -> p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } else { + OfflinePlayer[] players = Bukkit.getOfflinePlayers(); + if(players.length != 0) { + String arg = args.get(0).toLowerCase(); + Arrays.stream(players).parallel() + .filter(p -> p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + } + } else if(args.size() == 3) { + Set currencies = this.currenciesManager.getCurrenciesIDs(); + if(!currencies.isEmpty()) { + String arg = args.get(2).toLowerCase(); + currencies.parallelStream() + .filter(c -> c.toLowerCase().startsWith(arg)) + .forEach(c -> output.add(c)); + } + } else return null; + + return output; + } + + + +} diff --git a/src/main/java/ru/soknight/peconomy/command/SubcommandHandler.java b/src/main/java/ru/soknight/peconomy/command/SubcommandHandler.java new file mode 100644 index 0000000..302d720 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/SubcommandHandler.java @@ -0,0 +1,35 @@ +package ru.soknight.peconomy.command; + +import ru.soknight.lib.command.AbstractSubcommandsHandler; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.peconomy.PEconomy; +import ru.soknight.peconomy.command.sub.CommandAdd; +import ru.soknight.peconomy.command.sub.CommandHelp; +import ru.soknight.peconomy.command.sub.CommandHistory; +import ru.soknight.peconomy.command.sub.CommandInfo; +import ru.soknight.peconomy.command.sub.CommandReload; +import ru.soknight.peconomy.command.sub.CommandReset; +import ru.soknight.peconomy.command.sub.CommandSet; +import ru.soknight.peconomy.command.sub.CommandTake; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.database.DatabaseManager; + +public class SubcommandHandler extends AbstractSubcommandsHandler { + + public SubcommandHandler(PEconomy plugin, CurrenciesManager currenciesManager, DatabaseManager databaseManager, + Configuration config, Messages messages) { + + super(messages); + + super.setExecutor("help", new CommandHelp(messages)); + super.setExecutor("history", new CommandHistory(plugin, databaseManager, currenciesManager, config, messages)); + super.setExecutor("info", new CommandInfo(plugin, databaseManager, currenciesManager, config, messages)); + super.setExecutor("add", new CommandAdd(databaseManager, currenciesManager, config, messages)); + super.setExecutor("set", new CommandSet(databaseManager, currenciesManager, config, messages)); + super.setExecutor("reset", new CommandReset(databaseManager, currenciesManager, config, messages)); + super.setExecutor("take", new CommandTake(databaseManager, currenciesManager, config, messages)); + super.setExecutor("reload", new CommandReload(plugin, messages)); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/sub/CommandAdd.java b/src/main/java/ru/soknight/peconomy/command/sub/CommandAdd.java new file mode 100644 index 0000000..362aa63 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/sub/CommandAdd.java @@ -0,0 +1,168 @@ +package ru.soknight.peconomy.command.sub; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedSubcommandExecutor; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.CommandExecutionData; +import ru.soknight.lib.validation.validator.ArgsCountValidator; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.command.tool.SourceFormatter; +import ru.soknight.peconomy.command.validation.AmountValidator; +import ru.soknight.peconomy.command.validation.CurrencyValidator; +import ru.soknight.peconomy.command.validation.WalletExecutionData; +import ru.soknight.peconomy.command.validation.WalletValidator; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.TransactionType; +import ru.soknight.peconomy.database.Wallet; + +public class CommandAdd extends ExtendedSubcommandExecutor { + + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + private final Configuration config; + private final Messages messages; + + public CommandAdd(DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + + super(messages); + + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + this.config = config; + this.messages = messages; + + String permmsg = messages.get("error.no-permissions"); + String argsmsg = messages.get("error.wrong-syntax"); + String walletmsg = messages.get("error.unknown-wallet"); + String amountmsg = messages.get("error.arg-is-not-float"); + String currencymsg = messages.get("error.unknown-currency"); + + Validator permval = new PermissionValidator("peco.command.add", permmsg); + Validator argsval = new ArgsCountValidator(3, argsmsg); + Validator walletval = new WalletValidator(databaseManager, walletmsg); + Validator amountval = new AmountValidator(amountmsg); + Validator currencyval = new CurrencyValidator(currenciesManager, currencymsg); + + super.addValidators(permval, argsval, walletval, amountval, currencyval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + String owner = args.get(0), amstr = args.get(1), currencyid = args.get(2); + + CommandExecutionData data = new WalletExecutionData(sender, args, owner, currencyid, amstr); + if(!validateExecution(data)) return; + + // Preparing amount and source + float amount = Float.parseFloat(amstr); + String source = sender instanceof Player ? sender.getName() : config.getColoredString("console-source"); + + // Getting some values + Wallet wallet = databaseManager.getWallet(owner); + + float pre = wallet.getAmount(currencyid); + float post = pre + amount; + + CurrencyInstance currency = currenciesManager.getCurrency(currencyid); + + // Formatting values + String symbol = currency.getSymbol(); + String prestr = AmountFormatter.format(pre); + + // Checking for balance limit + float limit = currency.getLimit(); + if(limit != 0 && post > limit) { + String limitstr = AmountFormatter.format(limit); + messages.sendFormatted(sender, "add.limit", + "%amount%", prestr, + "%currency%", symbol, + "%player%", owner, + "%limit%", limitstr); + return; + } + + // Updating DB + wallet.addAmount(currencyid, amount); + databaseManager.updateWallet(wallet); + + // Formatting values + String amountstr = AmountFormatter.format(amount); + String poststr = AmountFormatter.format(post); + String operation = messages.get("operation.increase"); + + // Saving transaction + Transaction transaction = new Transaction(owner, currencyid, TransactionType.ADD, pre, post, source); + databaseManager.saveTransaction(transaction); + + int id = transaction.getId(); + + // Sending messages to sender and wallet owner if he is online + messages.sendFormatted(sender, "add.other", + "%amount%", amountstr, + "%currency%", symbol, + "%player%", owner, + "%from%", prestr, + "%operation%", operation, + "%to%", poststr, + "%id%", id); + + OfflinePlayer offlinetarget = Bukkit.getOfflinePlayer(owner); + if(offlinetarget.isOnline()) + messages.sendFormatted(offlinetarget.getPlayer(), "add.self", + "%amount%", amountstr, + "%currency%", symbol, + "%player%", owner, + "%from%", prestr, + "%operation%", operation, + "%to%", poststr, + "%source%", SourceFormatter.format(config, source, offlinetarget.getPlayer()), + "%id%", id); + } + + @Override + public List executeTabCompletion(CommandSender sender, CommandArguments args) { + if(!validateTabCompletion(sender, args)) return null; + + List output = new ArrayList<>(); + + if(args.size() == 1) { + Collection players = Bukkit.getOnlinePlayers(); + if(!players.isEmpty()) { + String arg = args.get(0).toLowerCase(); + players.parallelStream() + .filter(p -> p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + } else if(args.size() == 3) { + Set currencies = this.currenciesManager.getCurrenciesIDs(); + if(!currencies.isEmpty()) { + String arg = args.get(2).toLowerCase(); + currencies.parallelStream() + .filter(c -> c.toLowerCase().startsWith(arg)) + .forEach(c -> output.add(c)); + } + } else return null; + + return output; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/sub/CommandHelp.java b/src/main/java/ru/soknight/peconomy/command/sub/CommandHelp.java new file mode 100644 index 0000000..3f4e871 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/sub/CommandHelp.java @@ -0,0 +1,61 @@ +package ru.soknight.peconomy.command.sub; + +import org.bukkit.command.CommandSender; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedSubcommandExecutor; +import ru.soknight.lib.command.help.HelpMessage; +import ru.soknight.lib.command.help.HelpMessageFactory; +import ru.soknight.lib.command.help.HelpMessageItem; +import ru.soknight.lib.command.placeholder.Placeholder; +import ru.soknight.lib.command.placeholder.SimplePlaceholder; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.validator.PermissionValidator; + +public class CommandHelp extends ExtendedSubcommandExecutor { + + private final HelpMessage message; + + public CommandHelp(Messages messages) { + super(messages); + + HelpMessageFactory factory = new HelpMessageFactory(messages, "peco.command.%command%"); + + Placeholder pplayerreq = new SimplePlaceholder(messages, "player"); + Placeholder pplayeropt = new SimplePlaceholder(messages, "player-optional"); + Placeholder pcurrency = new SimplePlaceholder(messages, "currency"); + Placeholder pamount = new SimplePlaceholder(messages, "amount"); + Placeholder ppage = new SimplePlaceholder(messages, "page"); + Placeholder pid = new SimplePlaceholder(messages, "id"); + + String d = "help.descriptions."; + HelpMessageItem help = new HelpMessageItem("peco help", messages, d + "help"); + HelpMessageItem history = new HelpMessageItem("peco history", messages, d + "history", pplayerreq, ppage); + HelpMessageItem info = new HelpMessageItem("peco info", messages, d + "info", pid); + HelpMessageItem add = new HelpMessageItem("peco add", messages, d + "add", pplayerreq, pamount, pcurrency); + HelpMessageItem set = new HelpMessageItem("peco set", messages, d + "set", pplayerreq, pamount, pcurrency); + HelpMessageItem reset = new HelpMessageItem("peco reset", messages, d + "reset", pplayerreq, pcurrency); + HelpMessageItem take = new HelpMessageItem("peco take", messages, d + "take", pplayerreq, pamount, pcurrency); + HelpMessageItem reload = new HelpMessageItem("peco reload", messages, d + "reload"); + HelpMessageItem balance = new HelpMessageItem("balance", messages, pplayeropt); + HelpMessageItem pay = new HelpMessageItem("pay", messages, pplayerreq, pamount, pcurrency); + + factory.appendItems(true, help, history, info, add, set, reset, take, reload, balance, pay); + + this.message = factory.build(); + + String permmsg = messages.get("error.no-permissions"); + + PermissionValidator permval = new PermissionValidator("peco.command.help", permmsg); + + super.addValidators(permval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + if(!validateExecution(sender, args)) return; + + message.send(sender); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/sub/CommandHistory.java b/src/main/java/ru/soknight/peconomy/command/sub/CommandHistory.java new file mode 100644 index 0000000..a6080e3 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/sub/CommandHistory.java @@ -0,0 +1,207 @@ +package ru.soknight.peconomy.command.sub; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedSubcommandExecutor; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.tool.CollectionsTool; +import ru.soknight.lib.validation.validator.ArgsCountValidator; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.PEconomy; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.command.tool.SourceFormatter; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.TransactionType; + +public class CommandHistory extends ExtendedSubcommandExecutor { + + private final Logger logger; + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + private final Configuration config; + private final Messages messages; + + public CommandHistory(PEconomy plugin, DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + + super(messages); + + this.logger = plugin.getLogger(); + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + this.config = config; + this.messages = messages; + + String permmsg = messages.get("error.no-permissions"); + String argsmsg = messages.get("error.wrong-syntax"); + + Validator permval = new PermissionValidator("peco.command.history", permmsg); + Validator argsval = new ArgsCountValidator(1, argsmsg); + + super.addValidators(permval, argsval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + if(!validateExecution(sender, args)) return; + + String owner = args.get(0); + boolean other = false; + + // Check for trying to see other history + if(!owner.equals(sender.getName())) { + if(!sender.hasPermission("peco.command.history.other")) { + messages.getAndSend(sender, "history.other-history"); + return; + } + + OfflinePlayer offline = Bukkit.getOfflinePlayer(owner); + if(offline != null && !offline.isOnline() && !sender.hasPermission("peco.command.history.offline")) { + messages.getAndSend(sender, "history.offline-history"); + return; + } + + other = true; + } + + // Check for wallet exist + if(!databaseManager.hasWallet(owner)) { + messages.sendFormatted(sender, "error.unknown-wallet", "%player%", owner); + return; + } + + // Getting target page + int page = 1; + if(args.size() > 1) { + try { + page = Integer.parseInt(args.get(1)); + } catch (NumberFormatException e) { + messages.sendFormatted(sender, "error.arg-is-not-int", "%arg%", args.get(1)); + return; + } + } + + List transactions = databaseManager.getWalletTransactions(owner); + if(transactions == null) { + if(other) + messages.sendFormatted(sender, "history.empty-other", "%player%", owner); + else messages.getAndSend(sender, "history.empty-self"); + return; + } + + int size = config.getInt("messages.list-size"); + List onpage = CollectionsTool.getSubList(transactions, size, page); + + if(onpage.isEmpty()) { + messages.sendFormatted(sender, "history.empty-page", "%page%", page); + return; + } + + int total = transactions.size() / size; + if(transactions.size() % size != 0) total++; + + String formatPattern = config.getColoredString("transactions-history.date-format"); + DateFormat tempFormat; + + try { + tempFormat = new SimpleDateFormat(formatPattern); + } catch (Exception e) { + logger.severe("Hey! You use invalid transaction date format: " + formatPattern); + tempFormat = new SimpleDateFormat("dd.MM.yy - kk:mm:ss"); + } + + final DateFormat format = tempFormat; + + String body = messages.get("history.body"); + List output = new ArrayList<>(); + + onpage.forEach(t -> { + int id = t.getId(); + String source = SourceFormatter.format(config, t.getSource(), sender); + String date = t.formatDate(format); + + float pre = t.getPreBalance(); + float post = t.getPostBalance(); + + String operation = pre < post ? "increase" : "decrease"; + operation = messages.get("operation." + operation); + + TransactionType type = t.getType(); + String action = messages.getFormatted("action." + type.toString().toLowerCase(), "%source%", source); + + CurrencyInstance currency = currenciesManager.getCurrency(t.getCurrency()); + String symbol = currency == null ? "?" : currency.getSymbol(); + + output.add(messages.format(body, + "%id%", id, + "%date%", date, + "%from%", AmountFormatter.format(pre), + "%currency%", symbol, + "%operation%", operation, + "%to%", AmountFormatter.format(post), + "%action%", action + )); + }); + + String header = other + ? messages.getFormatted("history.header-other", "%player%", owner, "%page%", page, "%total%", total) + : messages.getFormatted("history.header-self", "%page%", page, "%total%", total); + + String footer = messages.get("history.footer-" + (other ? "other" : "self")); + + messages.send(sender, header); + output.forEach(b -> messages.send(sender, b)); + messages.send(sender, footer); + } + + @Override + public List executeTabCompletion(CommandSender sender, CommandArguments args) { + if(args.size() != 1 || !validateTabCompletion(sender, args)) return null; + + List output = new ArrayList<>(); + + // Adds other online players if interaction with them is permitted + if(sender.hasPermission("peco.command.history.other")) { + Collection players = Bukkit.getOnlinePlayers(); + if(!players.isEmpty()) { + String arg = args.get(0).toLowerCase(); + players.parallelStream() + .filter(p -> p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + // Adds other offline players if interaction with them is permitted + if(sender.hasPermission("peco.command.history.offline")) { + OfflinePlayer[] oplayers = Bukkit.getOfflinePlayers(); + if(oplayers.length != 0) { + String arg = args.get(0).toLowerCase(); + Arrays.stream(oplayers).parallel() + .filter(p -> !p.isOnline() && p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + } + } else if(sender instanceof Player) + output.add(sender.getName()); + + return output; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/sub/CommandInfo.java b/src/main/java/ru/soknight/peconomy/command/sub/CommandInfo.java new file mode 100644 index 0000000..777ef32 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/sub/CommandInfo.java @@ -0,0 +1,160 @@ +package ru.soknight.peconomy.command.sub; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedSubcommandExecutor; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.validator.ArgsCountValidator; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.PEconomy; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.command.tool.SourceFormatter; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.TransactionType; + +public class CommandInfo extends ExtendedSubcommandExecutor { + + private final Logger logger; + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + private final Configuration config; + private final Messages messages; + + private final String header; + private final Set keys; + private final String footer; + + public CommandInfo(PEconomy plugin, DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + + super(messages); + + this.logger = plugin.getLogger(); + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + this.config = config; + this.messages = messages; + + this.header = messages.get("info.header"); + this.keys = messages.getFileConfig().getConfigurationSection("info.list").getKeys(false); + this.footer = messages.get("info.footer"); + + String permmsg = messages.get("error.no-permissions"); + String argsmsg = messages.get("error.wrong-syntax"); + + Validator permval = new PermissionValidator("peco.command.info", permmsg); + Validator argsval = new ArgsCountValidator(1, argsmsg); + + super.addValidators(permval, argsval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + if(!validateExecution(sender, args)) return; + + // Getting transaction ID + int id = 1; + + try { + id = Integer.parseInt(args.get(0)); + } catch (NumberFormatException e) { + messages.sendFormatted(sender, "error.arg-is-not-int", "%arg%", args.get(0)); + return; + } + + // Getting transaction if it's exist + Transaction transaction = databaseManager.getTransactionByID(id); + if(transaction == null) { + messages.sendFormatted(sender, "info.not-found", "%id%", id); + return; + } + + String owner = transaction.getOwner(); + + // Checking for trying see transaction of other wallet + if(!owner.equals(sender.getName())) { + if(!sender.hasPermission("peco.command.info.other")) { + messages.getAndSend(sender, "info.other-info"); + return; + } + + OfflinePlayer offline = Bukkit.getOfflinePlayer(owner); + if(offline != null && !offline.isOnline() && !sender.hasPermission("peco.command.info.offline")) { + messages.getAndSend(sender, "info.offline-info"); + return; + } + } + + String formatPattern = config.getColoredString("transactions-history.date-format"); + DateFormat tempFormat; + + try { + tempFormat = new SimpleDateFormat(formatPattern); + } catch (Exception e) { + logger.severe("Hey! You use invalid transaction date format: " + formatPattern); + tempFormat = new SimpleDateFormat("dd.MM.yy - kk:mm:ss"); + } + + final DateFormat format = tempFormat; + + String source = SourceFormatter.format(config, transaction.getSource(), sender); + String date = transaction.formatDate(format); + + float pre = transaction.getPreBalance(); + float post = transaction.getPostBalance(); + + String operation = pre < post ? "increase" : "decrease"; + operation = messages.get("operation." + operation); + + TransactionType type = transaction.getType(); + String action = messages.getFormatted("action." + type.toString().toLowerCase(), "%source%", source); + + CurrencyInstance currency = currenciesManager.getCurrency(transaction.getCurrency()); + String symbol = currency == null ? "?" : currency.getSymbol(); + + // Preparing messages data + Map data = new HashMap<>(); + data.put("id", new Object[] { "%id%", id }); + data.put("owner", new Object[] { "%owner%", owner }); + data.put("action", new Object[] { "%action%", action }); + data.put("pre", new Object[] { "%pre%", AmountFormatter.format(pre), "%symbol%", symbol }); + data.put("post", new Object[] { "%post%", AmountFormatter.format(post), "%symbol%", symbol }); + data.put("currency", new Object[] { "%currency%", transaction.getCurrency(), "%symbol%", symbol }); + data.put("date", new Object[] { "%date%", date }); + + /* + * Sending info messages block + */ + + messages.send(sender, header); + + if(!keys.isEmpty()) + keys.forEach(k -> { + String message = data.containsKey(k) + ? messages.getFormatted("info.list." + k, data.get(k)) + : messages.get("info.list." + k); + + messages.send(sender, message); + }); + + messages.send(sender, footer); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/sub/CommandReload.java b/src/main/java/ru/soknight/peconomy/command/sub/CommandReload.java new file mode 100644 index 0000000..891533b --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/sub/CommandReload.java @@ -0,0 +1,39 @@ +package ru.soknight.peconomy.command.sub; + +import org.bukkit.command.CommandSender; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedSubcommandExecutor; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.PEconomy; + +public class CommandReload extends ExtendedSubcommandExecutor { + + private final PEconomy plugin; + private final Messages messages; + + public CommandReload(PEconomy plugin, Messages messages) { + super(messages); + + this.plugin = plugin; + this.messages = messages; + + String permmsg = messages.get("error.no-permissions"); + + Validator permval = new PermissionValidator("peco.command.reload", permmsg); + + super.addValidators(permval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + if(!validateExecution(sender, args)) return; + + plugin.refresh(); + + messages.getAndSend(sender, "reload-success"); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/sub/CommandReset.java b/src/main/java/ru/soknight/peconomy/command/sub/CommandReset.java new file mode 100644 index 0000000..c0b215b --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/sub/CommandReset.java @@ -0,0 +1,150 @@ +package ru.soknight.peconomy.command.sub; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedSubcommandExecutor; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.CommandExecutionData; +import ru.soknight.lib.validation.validator.ArgsCountValidator; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.command.tool.SourceFormatter; +import ru.soknight.peconomy.command.validation.CurrencyValidator; +import ru.soknight.peconomy.command.validation.WalletExecutionData; +import ru.soknight.peconomy.command.validation.WalletValidator; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.TransactionType; +import ru.soknight.peconomy.database.Wallet; + +public class CommandReset extends ExtendedSubcommandExecutor { + + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + private final Configuration config; + private final Messages messages; + + public CommandReset(DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + + super(messages); + + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + this.config = config; + this.messages = messages; + + String permmsg = messages.get("error.no-permissions"); + String argsmsg = messages.get("error.wrong-syntax"); + String walletmsg = messages.get("error.unknown-wallet"); + String currencymsg = messages.get("error.unknown-currency"); + + Validator permval = new PermissionValidator("peco.command.reset", permmsg); + Validator argsval = new ArgsCountValidator(2, argsmsg); + Validator walletval = new WalletValidator(databaseManager, walletmsg); + Validator currencyval = new CurrencyValidator(currenciesManager, currencymsg); + + super.addValidators(permval, argsval, walletval, currencyval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + String owner = args.get(0), currencyid = args.get(1); + + CommandExecutionData data = new WalletExecutionData(sender, args, owner, currencyid, null); + if(!validateExecution(data)) return; + + // Preparing amount and source + String source = sender instanceof Player ? sender.getName() : config.getColoredString("console-source"); + + // Getting some values + Wallet wallet = databaseManager.getWallet(owner); + + float pre = wallet.getAmount(currencyid); + + CurrencyInstance currency = currenciesManager.getCurrency(currencyid); + + // Formatting values + String symbol = currency.getSymbol(); + String prestr = AmountFormatter.format(pre); + + // Checking for already empty balance + if(pre == 0F) { + messages.sendFormatted(sender, "reset.already", "%player%", owner); + return; + } + + // Updating DB + wallet.resetWallet(currencyid); + databaseManager.updateWallet(wallet); + + // Formatting values + String operation = messages.get("operation.decrease"); + + // Saving transaction + Transaction transaction = new Transaction(owner, currencyid, TransactionType.RESET, pre, 0F, source); + databaseManager.saveTransaction(transaction); + + int id = transaction.getId(); + + // Sending messages to sender and wallet owner if he is online + messages.sendFormatted(sender, "reset.other", + "%currency%", symbol, + "%player%", owner, + "%from%", prestr, + "%operation%", operation, + "%id%", id); + + OfflinePlayer offlinetarget = Bukkit.getOfflinePlayer(owner); + if(offlinetarget.isOnline()) + messages.sendFormatted(offlinetarget.getPlayer(), "reset.self", + "%currency%", symbol, + "%from%", prestr, + "%operation%", operation, + "%source%", SourceFormatter.format(config, source, offlinetarget.getPlayer()), + "%id%", id); + } + + @Override + public List executeTabCompletion(CommandSender sender, CommandArguments args) { + if(!validateTabCompletion(sender, args)) return null; + + List output = new ArrayList<>(); + + if(args.size() == 1) { + Collection players = Bukkit.getOnlinePlayers(); + if(!players.isEmpty()) { + String arg = args.get(0).toLowerCase(); + players.parallelStream() + .filter(p -> p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + } else if(args.size() == 2) { + Set currencies = this.currenciesManager.getCurrenciesIDs(); + if(!currencies.isEmpty()) { + String arg = args.get(1).toLowerCase(); + currencies.parallelStream() + .filter(c -> c.toLowerCase().startsWith(arg)) + .forEach(c -> output.add(c)); + } + } else return null; + + return output; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/sub/CommandSet.java b/src/main/java/ru/soknight/peconomy/command/sub/CommandSet.java new file mode 100644 index 0000000..aca163e --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/sub/CommandSet.java @@ -0,0 +1,171 @@ +package ru.soknight.peconomy.command.sub; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedSubcommandExecutor; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.CommandExecutionData; +import ru.soknight.lib.validation.validator.ArgsCountValidator; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.command.tool.SourceFormatter; +import ru.soknight.peconomy.command.validation.AmountValidator; +import ru.soknight.peconomy.command.validation.CurrencyValidator; +import ru.soknight.peconomy.command.validation.WalletExecutionData; +import ru.soknight.peconomy.command.validation.WalletValidator; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.TransactionType; +import ru.soknight.peconomy.database.Wallet; + +public class CommandSet extends ExtendedSubcommandExecutor { + + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + private final Configuration config; + private final Messages messages; + + public CommandSet(DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + + super(messages); + + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + this.config = config; + this.messages = messages; + + String permmsg = messages.get("error.no-permissions"); + String argsmsg = messages.get("error.wrong-syntax"); + String walletmsg = messages.get("error.unknown-wallet"); + String amountmsg = messages.get("error.arg-is-not-float"); + String currencymsg = messages.get("error.unknown-currency"); + + Validator permval = new PermissionValidator("peco.command.set", permmsg); + Validator argsval = new ArgsCountValidator(3, argsmsg); + Validator walletval = new WalletValidator(databaseManager, walletmsg); + Validator amountval = new AmountValidator(amountmsg); + Validator currencyval = new CurrencyValidator(currenciesManager, currencymsg); + + super.addValidators(permval, argsval, walletval, amountval, currencyval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + String owner = args.get(0), amstr = args.get(1), currencyid = args.get(2); + + CommandExecutionData data = new WalletExecutionData(sender, args, owner, currencyid, amstr); + if(!validateExecution(data)) return; + + // Preparing amount and source + float amount = Float.parseFloat(amstr); + String source = sender instanceof Player ? sender.getName() : config.getColoredString("console-source"); + + // Getting some values + Wallet wallet = databaseManager.getWallet(owner); + + float pre = wallet.getAmount(currencyid); + + CurrencyInstance currency = currenciesManager.getCurrency(currencyid); + + // Formatting values + String symbol = currency.getSymbol(); + String prestr = AmountFormatter.format(pre); + + // Checking for balance limit + float limit = currency.getLimit(); + if(limit != 0 && amount > limit) { + String limitstr = AmountFormatter.format(limit); + messages.sendFormatted(sender, "set.limit", + "%currency%", symbol, + "%limit%", limitstr); + return; + } + + // Checking for balance already + String amountstr = AmountFormatter.format(amount); + if(pre == amount) { + messages.sendFormatted(sender, "set.limit", + "%player%", owner, + "%currency%", symbol, + "%amount%", amountstr); + return; + } + + // Updating DB + wallet.setAmount(currencyid, amount); + databaseManager.updateWallet(wallet); + + // Formatting values + String operation = messages.get("operation." + (pre < amount ? "increase" : "decrease")); + + // Saving transaction + Transaction transaction = new Transaction(owner, currencyid, TransactionType.SET, pre, amount, source); + databaseManager.saveTransaction(transaction); + + int id = transaction.getId(); + + // Sending messages to sender and wallet owner if he is online + messages.sendFormatted(sender, "set.other", + "%currency%", symbol, + "%player%", owner, + "%from%", prestr, + "%operation%", operation, + "%to%", amountstr, + "%id%", id); + + OfflinePlayer offlinetarget = Bukkit.getOfflinePlayer(owner); + if(offlinetarget.isOnline()) + messages.sendFormatted(offlinetarget.getPlayer(), "set.self", + "%currency%", symbol, + "%player%", owner, + "%from%", prestr, + "%operation%", operation, + "%to%", amountstr, + "%source%", SourceFormatter.format(config, source, offlinetarget.getPlayer()), + "%id%", id); + } + + @Override + public List executeTabCompletion(CommandSender sender, CommandArguments args) { + if(!validateTabCompletion(sender, args)) return null; + + List output = new ArrayList<>(); + + if(args.size() == 1) { + Collection players = Bukkit.getOnlinePlayers(); + if(!players.isEmpty()) { + String arg = args.get(0).toLowerCase(); + players.parallelStream() + .filter(p -> p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + } else if(args.size() == 3) { + Set currencies = this.currenciesManager.getCurrenciesIDs(); + if(!currencies.isEmpty()) { + String arg = args.get(2).toLowerCase(); + currencies.parallelStream() + .filter(c -> c.toLowerCase().startsWith(arg)) + .forEach(c -> output.add(c)); + } + } else return null; + + return output; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/sub/CommandTake.java b/src/main/java/ru/soknight/peconomy/command/sub/CommandTake.java new file mode 100644 index 0000000..29be83a --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/sub/CommandTake.java @@ -0,0 +1,166 @@ +package ru.soknight.peconomy.command.sub; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.command.ExtendedSubcommandExecutor; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.lib.validation.CommandExecutionData; +import ru.soknight.lib.validation.validator.ArgsCountValidator; +import ru.soknight.lib.validation.validator.PermissionValidator; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.command.tool.SourceFormatter; +import ru.soknight.peconomy.command.validation.AmountValidator; +import ru.soknight.peconomy.command.validation.CurrencyValidator; +import ru.soknight.peconomy.command.validation.WalletExecutionData; +import ru.soknight.peconomy.command.validation.WalletValidator; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.TransactionType; +import ru.soknight.peconomy.database.Wallet; + +public class CommandTake extends ExtendedSubcommandExecutor { + + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + private final Configuration config; + private final Messages messages; + + public CommandTake(DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + + super(messages); + + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + this.config = config; + this.messages = messages; + + String permmsg = messages.get("error.no-permissions"); + String argsmsg = messages.get("error.wrong-syntax"); + String walletmsg = messages.get("error.unknown-wallet"); + String amountmsg = messages.get("error.arg-is-not-float"); + String currencymsg = messages.get("error.unknown-currency"); + + Validator permval = new PermissionValidator("peco.command.take", permmsg); + Validator argsval = new ArgsCountValidator(3, argsmsg); + Validator walletval = new WalletValidator(databaseManager, walletmsg); + Validator amountval = new AmountValidator(amountmsg); + Validator currencyval = new CurrencyValidator(currenciesManager, currencymsg); + + super.addValidators(permval, argsval, walletval, amountval, currencyval); + } + + @Override + public void executeCommand(CommandSender sender, CommandArguments args) { + String owner = args.get(0), amstr = args.get(1), currencyid = args.get(2); + + CommandExecutionData data = new WalletExecutionData(sender, args, owner, currencyid, amstr); + if(!validateExecution(data)) return; + + // Preparing amount and source + float amount = Float.parseFloat(amstr); + String source = sender instanceof Player ? sender.getName() : config.getColoredString("console-source"); + + // Getting some values + Wallet wallet = databaseManager.getWallet(owner); + + float pre = wallet.getAmount(currencyid); + float post = pre - amount; + + CurrencyInstance currency = currenciesManager.getCurrency(currencyid); + + // Formatting values + String symbol = currency.getSymbol(); + String prestr = AmountFormatter.format(pre); + + // Checking for invalid balance + String amountstr = AmountFormatter.format(amount); + if(post < 0) { + messages.sendFormatted(sender, "take.not-enough", + "%amount%", prestr, + "%currency%", symbol, + "%player%", owner, + "%requested%", amountstr); + return; + } + + // Updating DB + wallet.takeAmount(currencyid, amount); + databaseManager.updateWallet(wallet); + + // Formatting values + String poststr = AmountFormatter.format(post); + String operation = messages.get("operation.decrease"); + + // Saving transaction + Transaction transaction = new Transaction(owner, currencyid, TransactionType.TAKE, pre, post, source); + databaseManager.saveTransaction(transaction); + + int id = transaction.getId(); + + // Sending messages to sender and wallet owner if he is online + messages.sendFormatted(sender, "take.other", + "%amount%", amountstr, + "%currency%", symbol, + "%player%", owner, + "%from%", prestr, + "%operation%", operation, + "%to%", poststr, + "%id%", id); + + OfflinePlayer offlinetarget = Bukkit.getOfflinePlayer(owner); + if(offlinetarget.isOnline()) + messages.sendFormatted(offlinetarget.getPlayer(), "take.self", + "%amount%", amountstr, + "%currency%", symbol, + "%player%", owner, + "%from%", prestr, + "%operation%", operation, + "%to%", poststr, + "%source%", SourceFormatter.format(config, source, offlinetarget.getPlayer()), + "%id%", id); + } + + @Override + public List executeTabCompletion(CommandSender sender, CommandArguments args) { + if(!validateTabCompletion(sender, args)) return null; + + List output = new ArrayList<>(); + + if(args.size() == 1) { + Collection players = Bukkit.getOnlinePlayers(); + if(!players.isEmpty()) { + String arg = args.get(0).toLowerCase(); + players.parallelStream() + .filter(p -> p.getName().toLowerCase().startsWith(arg)) + .forEach(p -> output.add(p.getName())); + } + } else if(args.size() == 3) { + Set currencies = this.currenciesManager.getCurrenciesIDs(); + if(!currencies.isEmpty()) { + String arg = args.get(2).toLowerCase(); + currencies.parallelStream() + .filter(c -> c.toLowerCase().startsWith(arg)) + .forEach(c -> output.add(c)); + } + } else return null; + + return output; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/tool/AmountFormatter.java b/src/main/java/ru/soknight/peconomy/command/tool/AmountFormatter.java new file mode 100644 index 0000000..46bc585 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/tool/AmountFormatter.java @@ -0,0 +1,11 @@ +package ru.soknight.peconomy.command.tool; + +public class AmountFormatter { + + private static final String format = "%.2f"; + + public static String format(float amount) { + return String.format(format, amount); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/tool/SourceFormatter.java b/src/main/java/ru/soknight/peconomy/command/tool/SourceFormatter.java new file mode 100644 index 0000000..e8f1fe3 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/tool/SourceFormatter.java @@ -0,0 +1,22 @@ +package ru.soknight.peconomy.command.tool; + +import org.bukkit.command.CommandSender; + +import ru.soknight.lib.configuration.Configuration; + +public class SourceFormatter { + + public static String format(Configuration config, String source, CommandSender viewing) { + if(!config.getBoolean("transaction-source-hiding.enabled")) + return source; + + else if(viewing.hasPermission("peco.transaction.sourcespy")) + return source; + + else if(!config.getList("transaction-source-hiding.staffs").contains(source)) + return source; + + else return config.getColoredString("transaction-source-hiding.value"); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/validation/AmountValidator.java b/src/main/java/ru/soknight/peconomy/command/validation/AmountValidator.java new file mode 100644 index 0000000..fd1db77 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/validation/AmountValidator.java @@ -0,0 +1,42 @@ +package ru.soknight.peconomy.command.validation; + +import ru.soknight.lib.validation.CommandExecutionData; +import ru.soknight.lib.validation.ValidationResult; +import ru.soknight.lib.validation.validator.Validator; + +public class AmountValidator implements Validator { + + private final String message; + + private final ValidationResult passed; + private final ValidationResult skipped; + + public AmountValidator(String message) { + this.message = message; + + this.passed = new ValidationResult(true); + this.skipped = new ValidationResult(false); + } + + @Override + public ValidationResult validate(CommandExecutionData data) { + if(!(data instanceof WalletExecutionData)) + return skipped; + + WalletExecutionData richdata = (WalletExecutionData) data; + String amount = richdata.getAmountAsString(); + + ValidationResult failed = new ValidationResult(false, message.replace("%argument%", amount)); + if(amount == null || amount.equals("")) return failed; + + boolean validated = false; + + try { + float a = Float.parseFloat(amount); + if(a > 0) validated = true; + } catch (NumberFormatException ignored) {} + + return validated ? passed : failed; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/validation/CurrencyValidator.java b/src/main/java/ru/soknight/peconomy/command/validation/CurrencyValidator.java new file mode 100644 index 0000000..242a348 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/validation/CurrencyValidator.java @@ -0,0 +1,38 @@ +package ru.soknight.peconomy.command.validation; + +import ru.soknight.lib.validation.CommandExecutionData; +import ru.soknight.lib.validation.ValidationResult; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.configuration.CurrenciesManager; + +public class CurrencyValidator implements Validator { + + private final CurrenciesManager currenciesManager; + private final String message; + + private final ValidationResult passed; + private final ValidationResult skipped; + + public CurrencyValidator(CurrenciesManager currenciesManager, String message) { + this.currenciesManager = currenciesManager; + this.message = message; + + this.passed = new ValidationResult(true); + this.skipped = new ValidationResult(false); + } + + @Override + public ValidationResult validate(CommandExecutionData data) { + if(!(data instanceof WalletExecutionData)) + return skipped; + + WalletExecutionData richdata = (WalletExecutionData) data; + String currency = richdata.getCurrency(); + + ValidationResult failed = new ValidationResult(false, message.replace("%currency%", currency)); + if(currency == null || currency.equals("")) return failed; + + return currenciesManager.isCurrency(currency.toLowerCase()) ? passed : failed; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/validation/WalletExecutionData.java b/src/main/java/ru/soknight/peconomy/command/validation/WalletExecutionData.java new file mode 100644 index 0000000..d2ac012 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/validation/WalletExecutionData.java @@ -0,0 +1,33 @@ +package ru.soknight.peconomy.command.validation; + +import org.bukkit.command.CommandSender; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import ru.soknight.lib.argument.CommandArguments; +import ru.soknight.lib.validation.CommandExecutionData; + +@Getter +@AllArgsConstructor +public class WalletExecutionData implements CommandExecutionData { + + @Getter private final CommandSender sender; + @Getter private final CommandArguments args; + + @Getter private final String owner; + @Getter private final String currency; + private final String amount; + + public String getAmountAsString() { + return this.amount; + } + + public Float getAmount() { + try { + return Float.parseFloat(this.amount); + } catch (NumberFormatException e) { + return null; + } + } + +} diff --git a/src/main/java/ru/soknight/peconomy/command/validation/WalletValidator.java b/src/main/java/ru/soknight/peconomy/command/validation/WalletValidator.java new file mode 100644 index 0000000..8ea0001 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/command/validation/WalletValidator.java @@ -0,0 +1,38 @@ +package ru.soknight.peconomy.command.validation; + +import ru.soknight.lib.validation.CommandExecutionData; +import ru.soknight.lib.validation.ValidationResult; +import ru.soknight.lib.validation.validator.Validator; +import ru.soknight.peconomy.database.DatabaseManager; + +public class WalletValidator implements Validator { + + private final DatabaseManager databaseManager; + private final String message; + + private final ValidationResult passed; + private final ValidationResult skipped; + + public WalletValidator(DatabaseManager databaseManager, String message) { + this.databaseManager = databaseManager; + this.message = message; + + this.passed = new ValidationResult(true); + this.skipped = new ValidationResult(false); + } + + @Override + public ValidationResult validate(CommandExecutionData data) { + if(!(data instanceof WalletExecutionData)) + return skipped; + + WalletExecutionData richdata = (WalletExecutionData) data; + String owner = richdata.getOwner(); + + ValidationResult failed = new ValidationResult(false, message.replace("%player%", owner)); + if(owner == null || owner.equals("")) return failed; + + return databaseManager.hasWallet(owner) ? passed : failed; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/configuration/CurrenciesManager.java b/src/main/java/ru/soknight/peconomy/configuration/CurrenciesManager.java new file mode 100644 index 0000000..58149c8 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/configuration/CurrenciesManager.java @@ -0,0 +1,93 @@ +package ru.soknight.peconomy.configuration; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import org.bukkit.configuration.ConfigurationSection; + +import lombok.Getter; +import ru.soknight.lib.configuration.AbstractConfiguration; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.peconomy.PEconomy; + +public class CurrenciesManager extends AbstractConfiguration { + + private Map currencies; + @Getter private CurrencyInstance vaultCurrency; + + private final Configuration config; + private final Logger logger; + + public CurrenciesManager(PEconomy plugin, Configuration config) { + super(plugin, "currencies.yml"); + this.currencies = new HashMap<>(); + + this.config = config; + this.logger = plugin.getLogger(); + refreshCurrencies(); + } + + public void refreshCurrencies() { + super.refresh(); + + this.currencies = new HashMap<>(); + + ConfigurationSection section = getFileConfig().getConfigurationSection("currencies"); + Set keys = section.getKeys(false); + + if(keys.isEmpty()) { + logger.severe("File currencies.yml is empty, there are no currencies to load."); + return; + } + + keys.forEach(id -> { + ConfigurationSection subsection = section.getConfigurationSection(id); + + String symbol = subsection.getString("symbol"); + if(symbol == null) { + logger.severe("Couldn't find the 'symbol' parameter for currency '" + id + "', loading skipped."); + return; + } + + float limit = (float) subsection.getDouble("max-amount", 0F); + float newbie = (float) subsection.getDouble("newbie-amount", 0F); + + CurrencyInstance currency = new CurrencyInstance(id, symbol, limit, newbie); + this.currencies.put(id, currency); + }); + + if(config.getBoolean("hooks.vault")) { + String vault = getFileConfig().getString("vault.currency"); + if(vault == null) + logger.info("Vault default currency is not specified, ignoring it."); + else if(!currencies.containsKey(vault)) + logger.severe("Failed to set vault default currency: Unknown currency '" + vault + "'."); + else { + this.vaultCurrency = currencies.get(vault); + logger.info("Currency '" + vault + "' will be used for Vault economy."); + } + } + + logger.info("Loaded " + this.currencies.size() + " currencies."); + } + + public CurrencyInstance getCurrency(String id) { + return this.currencies.get(id); + } + + public Set getCurrenciesIDs() { + return this.currencies.keySet(); + } + + public Collection getCurrencies() { + return this.currencies.values(); + } + + public boolean isCurrency(String id) { + return this.currencies.containsKey(id); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/configuration/CurrencyInstance.java b/src/main/java/ru/soknight/peconomy/configuration/CurrencyInstance.java new file mode 100644 index 0000000..23664d7 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/configuration/CurrencyInstance.java @@ -0,0 +1,22 @@ +package ru.soknight.peconomy.configuration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class CurrencyInstance { + + private final String ID; + private final String symbol; + private float limit; + private float newbieAmount; + + public CurrencyInstance(String id, String symbol) { + this.ID = id; + this.symbol = symbol; + this.limit = 0F; + this.newbieAmount = 0F; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/configuration/MessagesProvider.java b/src/main/java/ru/soknight/peconomy/configuration/MessagesProvider.java new file mode 100644 index 0000000..babf428 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/configuration/MessagesProvider.java @@ -0,0 +1,59 @@ +package ru.soknight.peconomy.configuration; + +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; + +import lombok.Getter; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.peconomy.PEconomy; + +@Getter +public class MessagesProvider { + + private static final List LOCALES = Arrays.asList("en", "ru"); + + private final PEconomy plugin; + private Messages messages; + + public MessagesProvider(PEconomy plugin, Configuration config) { + this.plugin = plugin; + + String locale = config.getString("messages.locale", "en").toLowerCase(); + + if(!LOCALES.contains(locale)) { + plugin.getLogger().severe("Unknown localization '" + locale + "', returns to English..."); + locale = "en"; + } + + String filename = "messages_" + locale + ".yml"; + InputStream source = plugin.getClass().getResourceAsStream("/locales/" + filename); + + if(source != null) + this.messages = new Messages(plugin, source, filename); + else plugin.getLogger().severe("Failed to get internal resource of messages file."); + } + + public void update(Configuration config) { + String locale = config.getString("messages.locale", "en").toLowerCase(); + + if(!LOCALES.contains(locale)) { + plugin.getLogger().severe("Unknown localization '" + locale + "', returns to English..."); + locale = "en"; + } + + String filename = "messages_" + locale + ".yml"; + InputStream source = plugin.getClass().getResourceAsStream("/locales/" + filename); + + if(source == null) { + plugin.getLogger().severe("Failed to get internal resource of messages file."); + return; + } + + messages.setSource(source); + messages.setFilename(filename); + messages.refresh(); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/database/Database.java b/src/main/java/ru/soknight/peconomy/database/Database.java new file mode 100644 index 0000000..3073f65 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/database/Database.java @@ -0,0 +1,67 @@ +package ru.soknight.peconomy.database; + +import java.io.File; +import java.sql.SQLException; + +import com.j256.ormlite.jdbc.JdbcConnectionSource; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.TableUtils; + +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.peconomy.PEconomy; + +public class Database { + + private final String url; + private final boolean useSQLite; + + private String user; + private String password; + + public Database(PEconomy plugin, Configuration config) throws Exception { + this.useSQLite = config.getBoolean("database.use-sqlite", true); + + if(!useSQLite) { + String host = config.getString("database.host", "localhost"); + String name = config.getString("database.name", "peconomy"); + + int port = config.getInt("database.port", 3306); + this.user = config.getString("database.user", "admin"); + this.password = config.getString("database.password", "peconomy"); + + String url = "jdbc:mysql://" + host + ":" + port + "/" + name; + + // Thanks to Ansandr for this issue + if(config.getBoolean("database.reconnect", true)) + url += "?autoReconnect=true"; + + this.url = url; + + Class.forName("com.mysql.jdbc.Driver").newInstance(); + } else { + String file = config.getString("database.file", "peconomy.db"); + + this.url = "jdbc:sqlite:" + plugin.getDataFolder().getPath() + File.separator + file; + + Class.forName("org.sqlite.JDBC").newInstance(); + } + + // Allowing only ORMLite errors logging + System.setProperty("com.j256.ormlite.logger.type", "LOCAL"); + System.setProperty("com.j256.ormlite.logger.level", "ERROR"); + + ConnectionSource source = getConnection(); + + TableUtils.createTableIfNotExists(source, Wallet.class); + TableUtils.createTableIfNotExists(source, Transaction.class); + + source.close(); + + plugin.getLogger().info("Database type " + (useSQLite ? "SQLite" : "MySQL") + " connected!"); + } + + public ConnectionSource getConnection() throws SQLException { + return useSQLite ? new JdbcConnectionSource(url) : new JdbcConnectionSource(url, user, password); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/database/DatabaseManager.java b/src/main/java/ru/soknight/peconomy/database/DatabaseManager.java new file mode 100644 index 0000000..17d2be6 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/database/DatabaseManager.java @@ -0,0 +1,144 @@ +package ru.soknight.peconomy.database; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.List; +import java.util.logging.Logger; + +import com.j256.ormlite.dao.Dao; +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.stmt.QueryBuilder; +import com.j256.ormlite.stmt.Where; +import com.j256.ormlite.support.ConnectionSource; + +import ru.soknight.peconomy.PEconomy; + +public class DatabaseManager { + + private final Logger logger; + private final ConnectionSource source; + + private final Dao ownersDao; + private final Dao transactionsDao; + + public DatabaseManager(PEconomy plugin, Database database) throws SQLException { + this.logger = plugin.getLogger(); + this.source = database.getConnection(); + + this.ownersDao = DaoManager.createDao(source, Wallet.class); + this.transactionsDao = DaoManager.createDao(source, Transaction.class); + } + + public void shutdown() { + try { + source.close(); + logger.info("Disconnected from database."); + } catch (IOException e) { + logger.severe("Failed to close database connection: " + e.getLocalizedMessage()); + } + } + + /* + * Players wallets + */ + + public boolean createWallet(Wallet owner) { + try { + return this.ownersDao.create(owner) != 0; + } catch (SQLException e) { + logger.severe("Failed to create wallet of player '" + owner.getOwner() + "': " + e.getMessage()); + return false; + } + } + + public Wallet getWallet(String name) { + try { + return this.ownersDao.queryForId(name); + } catch (SQLException e) { + logger.severe("Failed to get wallet for player '" + name + "': " + e.getMessage()); + return null; + } + } + + public int getWalletsCount() { + try { + return this.ownersDao.queryForAll().size(); + } catch (SQLException e) { + logger.severe("Failed to get wallets count: " + e.getMessage()); + return 0; + } + } + + public boolean hasWallet(String name) { + return getWallet(name) != null; + } + + public boolean transferWallet(Wallet owner, String name) { + try { + return this.ownersDao.updateId(owner, name) != 0; + } catch (SQLException e) { + logger.severe("Failed to transfer wallet of player '" + owner.getOwner() + "' to '" + name + "': " + + e.getMessage()); + return false; + } + } + + public boolean updateWallet(Wallet wallet) { + try { + return this.ownersDao.update(wallet) != 0; + } catch (SQLException e) { + logger.severe("Failed to update wallet of player '" + wallet.getOwner() + "': " + e.getMessage()); + return false; + } + } + + /* + * Transactions + */ + + public Transaction getTransactionByID(int id) { + try { + return this.transactionsDao.queryForId(id); + } catch (SQLException e) { + logger.severe("Failed to get transaction by ID #" + id + ": " + e.getMessage()); + return null; + } + } + +// public List getAllTransactions() { +// try { +// return this.transactionsDao.queryForAll(); +// } catch (SQLException e) { +// logger.severe("Failed to get all transactions: " + e.getMessage()); +// return null; +// } +// } + + public List getWalletTransactions(String owner) { + try { + QueryBuilder builder = transactionsDao.queryBuilder(); + Where where = builder.where(); + + where.eq("owner", owner); + + return builder.query(); + } catch (SQLException e) { + logger.severe("Failed to get transactions for " + owner + "'s wallet: " + e.getMessage()); + return null; + } + } + + public boolean hasTransactionWithID(int id) { + return getTransactionByID(id) != null; + } + + public boolean saveTransaction(Transaction transaction) { + try { + return this.transactionsDao.create(transaction) != 0; + } catch (SQLException e) { + logger.severe("Failed to save transaction: " + e.getMessage()); + return false; + } + } + +} diff --git a/src/main/java/ru/soknight/peconomy/database/Transaction.java b/src/main/java/ru/soknight/peconomy/database/Transaction.java new file mode 100644 index 0000000..2cafe72 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/database/Transaction.java @@ -0,0 +1,65 @@ +package ru.soknight.peconomy.database; + +import java.sql.Date; +import java.text.DateFormat; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@DatabaseTable(tableName = "transactions") +public class Transaction { + + @DatabaseField(generatedId = true) + private int id; + @DatabaseField + private String owner; + @DatabaseField + private String currency; + @Setter + @DatabaseField + private TransactionType type; + @DatabaseField + private float preBalance; + @DatabaseField + private float postBalance; + @DatabaseField + private String source; + @DatabaseField + private Date date; + + public Transaction(String owner, String currency, TransactionType type, float pre, float post, String source) { + this.owner = owner; + this.currency = currency; + this.type = type; + this.preBalance = pre; + this.postBalance = post; + this.source = source; + this.date = new Date(System.currentTimeMillis()); + } + + public String formatDate(DateFormat format) { + try { + return format.format(this.date); + } catch (Exception e) { + System.err.println("Failed to format transaction date with pattern '" + format + "': " + e.getMessage()); + return "???"; + } + } + + public String formatFloat(float source) { + return String.format("%.2f", source); + } + + public boolean isSuccessed() { + return this.type != TransactionType.FAILED; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/database/TransactionType.java b/src/main/java/ru/soknight/peconomy/database/TransactionType.java new file mode 100644 index 0000000..63ac803 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/database/TransactionType.java @@ -0,0 +1,7 @@ +package ru.soknight.peconomy.database; + +public enum TransactionType { + + ADD, SET, RESET, TAKE, PAYMENT, FAILED; + +} diff --git a/src/main/java/ru/soknight/peconomy/database/Wallet.java b/src/main/java/ru/soknight/peconomy/database/Wallet.java new file mode 100644 index 0000000..2ca4a45 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/database/Wallet.java @@ -0,0 +1,61 @@ +package ru.soknight.peconomy.database; + +import java.util.HashMap; + +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@DatabaseTable(tableName = "walletowners") +public class Wallet { + + @DatabaseField(id = true) + private String owner; + @DatabaseField(dataType = DataType.SERIALIZABLE) + private HashMap wallets; + + public Wallet(String owner) { + this.owner = owner; + this.wallets = new HashMap<>(); + } + + public void addAmount(String currency, float amount) { + float pre = getAmount(currency); + float post = pre + amount; + + this.wallets.put(currency, post); + } + + public float getAmount(String currency) { + return hasWallet(currency) ? this.wallets.get(currency) : 0F; + } + + public boolean hasAmount(String currency, float amount) { + return amount <= getAmount(currency); + } + + public boolean hasWallet(String currency) { + return this.wallets.containsKey(currency); + } + + public void resetWallet(String currency) { + this.wallets.put(currency, 0F); + } + + public void setAmount(String currency, float amount) { + this.wallets.put(currency, amount); + } + + public void takeAmount(String currency, float amount) { + float pre = getAmount(currency); + float post = pre - amount; + + if(post >= 0F) this.wallets.put(currency, post); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/hook/PEcoExpansion.java b/src/main/java/ru/soknight/peconomy/hook/PEcoExpansion.java new file mode 100644 index 0000000..7d31057 --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/hook/PEcoExpansion.java @@ -0,0 +1,70 @@ +package ru.soknight.peconomy.hook; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import lombok.Getter; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import ru.soknight.peconomy.PEconomy; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Wallet; + +public class PEcoExpansion extends PlaceholderExpansion { + + private static final List SUBIDS = Arrays.asList("balance", "has"); + + private final DatabaseManager databaseManager; + + @Getter private final String author; + @Getter private final String identifier; + @Getter private final String version; + + public PEcoExpansion(PEconomy plugin, DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + + this.author = "SoKnight"; + this.identifier = "peco"; + this.version = plugin.getDescription().getVersion(); + } + + @Override + public String onPlaceholderRequest(Player p, String id) { + if(p == null) return ChatColor.RED + "PLAYER IS NULL"; + + String[] parts = id.split("_"); + String name = p.getName(); + + String subid = parts[0].toLowerCase(); + + // Cancelling if placeholder is not implemented + if(!SUBIDS.contains(subid)) return ChatColor.RED + "INVALID PLACEHOLDER"; + + // Cancelling if currency is not specified + if(parts.length == 1) return ChatColor.RED + "CURRENCY IS NOT SPECIFIED"; + String currencyid = parts[1]; + + // Getting player's wallet + Wallet wallet = databaseManager.getWallet(name); + + // Handling implemented placeholders + switch (subid) { + case "balance": { + return wallet == null ? "0.00" : AmountFormatter.format(wallet.getAmount(currencyid)); + } + case "has": { + if(parts.length == 2) return ChatColor.RED + "AMOUNT IS NOT SPECIFIED"; + float amount = Float.parseFloat(parts[2]); + return wallet == null ? "false" : String.valueOf(wallet.hasAmount(currencyid, amount)); + } + default: + break; + } + + return ""; + } + +} diff --git a/src/main/java/ru/soknight/peconomy/hook/VaultEconomy.java b/src/main/java/ru/soknight/peconomy/hook/VaultEconomy.java new file mode 100644 index 0000000..48abe2b --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/hook/VaultEconomy.java @@ -0,0 +1,370 @@ +package ru.soknight.peconomy.hook; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; + +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import net.milkbowl.vault.economy.EconomyResponse.ResponseType; +import ru.soknight.lib.configuration.Configuration; +import ru.soknight.lib.configuration.Messages; +import ru.soknight.peconomy.command.tool.AmountFormatter; +import ru.soknight.peconomy.command.tool.SourceFormatter; +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Transaction; +import ru.soknight.peconomy.database.TransactionType; +import ru.soknight.peconomy.database.Wallet; + +public class VaultEconomy implements Economy { + + private final DatabaseManager databaseManager; + private final CurrencyInstance currency; + + private final Configuration config; + private final Messages messages; + + private final String plural; + private final String singular; + + public VaultEconomy(DatabaseManager databaseManager, CurrenciesManager currenciesManager, + Configuration config, Messages messages) { + + this.databaseManager = databaseManager; + this.currency = currenciesManager.getVaultCurrency(); + + this.config = config; + this.messages = messages; + + this.plural = currenciesManager.getColoredString("vault.plural"); + this.singular = currenciesManager.getColoredString("vault.singular"); + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public String getName() { + return "PEconomy"; + } + + @Override + public boolean hasBankSupport() { + return false; + } + + @Override + public int fractionalDigits() { + return 2; + } + + @Override + public String format(double amount) { + return AmountFormatter.format((float) amount); + } + + @Override + public String currencyNamePlural() { + return this.plural; + } + + @Override + public String currencyNameSingular() { + return this.singular; + } + + @Override + public boolean hasAccount(String playerName) { + return this.databaseManager.hasWallet(playerName); + } + + @Override + public boolean hasAccount(OfflinePlayer player) { + return hasAccount(player.getName()); + } + + @Override + public boolean hasAccount(String playerName, String worldName) { + return hasAccount(playerName); + } + + @Override + public boolean hasAccount(OfflinePlayer player, String worldName) { + return hasAccount(player.getName()); + } + + @Override + public double getBalance(String playerName) { + Wallet wallet = this.databaseManager.getWallet(playerName); + if(wallet == null) return 0; + + return wallet.getAmount(this.currency.getID()); + } + + @Override + public double getBalance(OfflinePlayer player) { + return getBalance(player.getName()); + } + + @Override + public double getBalance(String playerName, String world) { + return getBalance(playerName); + } + + @Override + public double getBalance(OfflinePlayer player, String world) { + return getBalance(player.getName()); + } + + @Override + public boolean has(String playerName, double amount) { + Wallet wallet = this.databaseManager.getWallet(playerName); + if(wallet == null) return false; + + return wallet.hasAmount(this.currency.getID(), (float) amount); + } + + @Override + public boolean has(OfflinePlayer player, double amount) { + return has(player.getName(), amount); + } + + @Override + public boolean has(String playerName, String worldName, double amount) { + return has(playerName, amount); + } + + @Override + public boolean has(OfflinePlayer player, String worldName, double amount) { + return has(player.getName(), amount); + } + + @Override + public EconomyResponse withdrawPlayer(String playerName, double amount) { + Wallet wallet = this.databaseManager.getWallet(playerName); + if(wallet == null) { + String message = messages.getFormatted("error.unknown-wallet", "%player%", playerName); + return new EconomyResponse(amount, 0D, ResponseType.FAILURE, message); + } + + float pre = wallet.getAmount(this.currency.getID()); + float post = pre - (float) amount; + + if(post < 0) { + String message = messages.getFormatted("take.not-enough", + "%amount%", AmountFormatter.format(pre), + "%currency%", currency.getSymbol(), + "%player%", playerName, + "%requested%", format(amount)); + return new EconomyResponse(amount, pre, ResponseType.FAILURE, message); + } + + wallet.takeAmount(this.currency.getID(), (float) amount); + databaseManager.updateWallet(wallet); + + // Saving transaction + Transaction transaction = new Transaction(playerName, currency.getID(), TransactionType.TAKE, pre, post, "#vault"); + databaseManager.saveTransaction(transaction); + + String message = messages.getFormatted("take.other", + "%amount%", format(amount), + "%currency%", currency.getSymbol(), + "%player%", playerName, + "%from%", AmountFormatter.format(pre), + "%operation%", messages.get("operation.decrease"), + "%to%", AmountFormatter.format(post), + "%id%", transaction.getId()); + + OfflinePlayer offline = Bukkit.getOfflinePlayer(playerName); + if(offline != null && offline.isOnline()) { + messages.sendFormatted(offline.getPlayer(), "take.self", + "%amount%", format(amount), + "%currency%", currency.getSymbol(), + "%player%", playerName, + "%from%", AmountFormatter.format(pre), + "%operation%", messages.get("operation.decrease"), + "%to%", AmountFormatter.format(post), + "%source%", SourceFormatter.format(config, "#vault", offline.getPlayer()), + "%id%", transaction.getId()); + } + + return new EconomyResponse(amount, post, ResponseType.SUCCESS, message); + } + + @Override + public EconomyResponse withdrawPlayer(OfflinePlayer player, double amount) { + return withdrawPlayer(player.getName(), amount); + } + + @Override + public EconomyResponse withdrawPlayer(String playerName, String worldName, double amount) { + return withdrawPlayer(playerName, amount); + } + + @Override + public EconomyResponse withdrawPlayer(OfflinePlayer player, String worldName, double amount) { + return withdrawPlayer(player.getName(), amount); + } + + @Override + public EconomyResponse depositPlayer(String playerName, double amount) { + Wallet wallet = this.databaseManager.getWallet(playerName); + if(wallet == null) { + String message = messages.getFormatted("error.unknown-wallet", "%player%", playerName); + return new EconomyResponse(amount, 0D, ResponseType.FAILURE, message); + } + + float pre = wallet.getAmount(this.currency.getID()); + float post = pre + (float) amount; + + float limit = currency.getLimit(); + if(limit != 0 && post > limit) { + String limitstr = AmountFormatter.format(limit); + String message = messages.getFormatted("add.limit", + "%amount%", AmountFormatter.format(pre), + "%currency%", currency.getSymbol(), + "%player%", playerName, + "%limit%", limitstr); + return new EconomyResponse(amount, pre, ResponseType.FAILURE, message); + } + + wallet.addAmount(this.currency.getID(), (float) amount); + databaseManager.updateWallet(wallet); + + // Saving transaction + Transaction transaction = new Transaction(playerName, currency.getID(), TransactionType.ADD, pre, post, "#vault"); + databaseManager.saveTransaction(transaction); + + String message = messages.getFormatted("add.other", + "%amount%", format(amount), + "%currency%", currency.getSymbol(), + "%player%", playerName, + "%from%", AmountFormatter.format(pre), + "%operation%", messages.get("operation.increase"), + "%to%", AmountFormatter.format(post), + "%id%", transaction.getId()); + + OfflinePlayer offline = Bukkit.getOfflinePlayer(playerName); + if(offline != null && offline.isOnline()) { + messages.sendFormatted(offline.getPlayer(), "add.self", + "%amount%", format(amount), + "%currency%", currency.getSymbol(), + "%player%", playerName, + "%from%", AmountFormatter.format(pre), + "%operation%", messages.get("operation.increase"), + "%to%", AmountFormatter.format(post), + "%source%", SourceFormatter.format(config, "#vault", offline.getPlayer()), + "%id%", transaction.getId()); + } + + return new EconomyResponse(amount, post, ResponseType.SUCCESS, message); + } + + @Override + public EconomyResponse depositPlayer(OfflinePlayer player, double amount) { + return depositPlayer(player.getName(), amount); + } + + @Override + public EconomyResponse depositPlayer(String playerName, String worldName, double amount) { + return depositPlayer(playerName, amount); + } + + @Override + public EconomyResponse depositPlayer(OfflinePlayer player, String worldName, double amount) { + return depositPlayer(player.getName(), amount); + } + + @Override + public EconomyResponse createBank(String name, String player) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse createBank(String name, OfflinePlayer player) { + return createBank(name, player.getName()); + } + + @Override + public EconomyResponse deleteBank(String name) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse bankBalance(String name) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse bankHas(String name, double amount) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse bankWithdraw(String name, double amount) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse bankDeposit(String name, double amount) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse isBankOwner(String name, String playerName) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse isBankOwner(String name, OfflinePlayer player) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse isBankMember(String name, String playerName) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public EconomyResponse isBankMember(String name, OfflinePlayer player) { + return new EconomyResponse(0, 0, ResponseType.NOT_IMPLEMENTED, ChatColor.RED + "Banks is not supported."); + } + + @Override + public List getBanks() { + return new ArrayList<>(); + } + + @Override + public boolean createPlayerAccount(String playerName) { + if(databaseManager.hasWallet(playerName)) return false; + + Wallet wallet = new Wallet(playerName); + wallet.setAmount(currency.getID(), currency.getNewbieAmount()); + databaseManager.createWallet(wallet); + return true; + } + + @Override + public boolean createPlayerAccount(OfflinePlayer player) { + return createPlayerAccount(player.getName()); + } + + @Override + public boolean createPlayerAccount(String playerName, String worldName) { + return createPlayerAccount(playerName); + } + + @Override + public boolean createPlayerAccount(OfflinePlayer player, String worldName) { + return createPlayerAccount(player.getName()); + } + +} diff --git a/src/main/java/ru/soknight/peconomy/listener/PlayerJoinListener.java b/src/main/java/ru/soknight/peconomy/listener/PlayerJoinListener.java new file mode 100644 index 0000000..77a6fab --- /dev/null +++ b/src/main/java/ru/soknight/peconomy/listener/PlayerJoinListener.java @@ -0,0 +1,54 @@ +package ru.soknight.peconomy.listener; + +import java.util.Collection; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; + +import ru.soknight.peconomy.configuration.CurrenciesManager; +import ru.soknight.peconomy.configuration.CurrencyInstance; +import ru.soknight.peconomy.database.DatabaseManager; +import ru.soknight.peconomy.database.Wallet; + +public class PlayerJoinListener implements Listener { + + private final Plugin plugin; + private final BukkitScheduler scheduler; + + private final DatabaseManager databaseManager; + private final CurrenciesManager currenciesManager; + + public PlayerJoinListener(Plugin plugin, DatabaseManager databaseManager, + CurrenciesManager currenciesManager) { + + this.plugin = plugin; + this.scheduler = plugin.getServer().getScheduler(); + + this.databaseManager = databaseManager; + this.currenciesManager = currenciesManager; + + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + String name = e.getPlayer().getName(); + + // Moved to async task + scheduler.runTaskAsynchronously(plugin, () -> { + if(databaseManager.hasWallet(name)) return; + + Wallet wallet = new Wallet(name); + + Collection currencies = currenciesManager.getCurrencies(); + if(!currencies.isEmpty()) + currencies.forEach(currency -> wallet.setAmount(currency.getID(), currency.getNewbieAmount())); + + databaseManager.createWallet(wallet); + }); + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..be569cd --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,95 @@ +# +# +# ██████╗░███████╗░█████╗░░█████╗░███╗░░██╗░█████╗░███╗░░░███╗██╗░░░██╗ +# ██╔══██╗██╔════╝██╔══██╗██╔══██╗████╗░██║██╔══██╗████╗░████║╚██╗░██╔╝ +# ██████╔╝█████╗░░██║░░╚═╝██║░░██║██╔██╗██║██║░░██║██╔████╔██║░╚████╔╝░ +# ██╔═══╝░██╔══╝░░██║░░██╗██║░░██║██║╚████║██║░░██║██║╚██╔╝██║░░╚██╔╝░░ +# ██║░░░░░███████╗╚█████╔╝╚█████╔╝██║░╚███║╚█████╔╝██║░╚═╝░██║░░░██║░░░ +# ╚═╝░░░░░╚══════╝░╚════╝░░╚════╝░╚═╝░░╚══╝░╚════╝░╚═╝░░░░░╚═╝░░░╚═╝░░░ +# +# +# This config file will be generated by PEconomy version ${project.version}. +# If this version older than current PEconomy plugin version, you should regenerate this config. +# You just need rename (to save, but you can delete it) this file and use '/peco reload' command. +# +# Github page: https://github.com/SoKnight/PEconomy +# SpigotMC page: https://www.spigotmc.org/resources/peconomy.73827/ +# +# Author and developer: SoKnight +# +# Available placeholders: +# - %peco_balance_CURRENCY% - Balance of currency on player's wallet +# - %peco_has_CURRENCY_AMOUNT% - Player has amount of currency on him balance or not (true/false) +# +# Configuration of database +database: + # Should plugin use MySQL database as data storage? + # Specify 'true' to use SQLite or 'false' to use MySQL + use-sqlite: true + + # Should plugin use reconnection to MySQL when current connection is unavailable. + # [!] Needed only for MySQL, if you will use SQLite, just ignore it. + # * Thanks to Ansandr for this issue :) + reconnect: true + + # MySQL configuration + host: localhost + port: 3306 + name: peconomy + user: admin + password: peconomy + + # SQLite configuration + file: database.db + +# Configuration of messages +messages: + # Messages localization, available: [en, ru] + # Write in discussion on spigotmc plugin page, if you can translate plugin to other languages + locale: en + + # Amount of rows per one list page for any list commands + list-size: 10 + +# Configuration of any plugin's hooks +# You want other hooks? Write me about it in plugin's discussion on SpigotMC page :) +hooks: + # Should plugin register self as economy provider for Vault? + vault: false + # Should plugin add own placeholders into PlaceholdersAPI? + papi: true + +# Source value for console-by transaction +# Example: 0.0 » 0.2 (added by #console), where #console is transaction source +console-source: "#console" + +# Should plugin send tab-completions with all wallets owners from database or not +# Specify 'true' as positive answer and 'false' to say that should send only online players names +extended-players-completions: true + +# Should plugin hide unknown stored balances currencies or not +# Example: You adds someone amount to player's wallet and after it you removes this currency from config and +# when this player wants check him balance, he don't see unknown currency balance if this feature will be enabled +# Else unknown currency will be marked with 'N/A' symbol +hide-unknown-currencies: false + +# Configuration of transactions history feature +transactions-history: + # Should plugin store transactions history for everyone player or not + enabled: true + + # Transaction date format + date-format: "dd.MM.yy - kk:mm:ss" + +# Configuration of transaction source hiding +transaction-source-hiding: + # Should this feature be enabled or not; by default nobody will be hided + enabled: true + + # Source value it it's hided + value: "#hided" + + # List of staffs which names will be hided + # It is case-sensetive: 'SoKnight' not equals 'soknight' + staffs: + - 'SoKnight' \ No newline at end of file diff --git a/src/main/resources/currencies.yml b/src/main/resources/currencies.yml new file mode 100644 index 0000000..edc57fe --- /dev/null +++ b/src/main/resources/currencies.yml @@ -0,0 +1,46 @@ +# ------------------------------------------------------------------ +# +# ${project.name} currencies file for their configuration +# Generated by version ${project.version} +# +# Author and developer: SoKnight +# +# Github wiki: https://github.com/SoKnight/${project.name}/wiki +# SpigotMC page: https://www.spigotmc.org/resources/peconomy.73827/ +# +# ------------------------------------------------------------------ +# +# Additional information see on Github wiki +# +# Configuration of Vault economy providing +# This feature can be disabled in config.yml -> hooks -> vault +vault: + # Default currency's ID to use in Vault economy provider + # Vault correctly supports only one currency, sorry :( + currency: 'dollars' + + # Plural currency name + plural: 'dollars' + + # Singular currency name + singular: 'dollar' + +# Manual configurations for every currency +currencies: + # UNIQUE ID of currency, will be used as currency command argument and tab-completion + dollars: + # Currency symbol(s) + symbol: '$' + # Max amount of this currency per player (optional, default: 0) + # * Specify 0 to disable this feature + # * It's float value, you can specify amount with float point (ex. 40000.5) + max-amount: 40000000 + # Amount of this currency per everyone newbies (optional, default: 0) + # * Specify 0 to disable this feature + # * It's float value, you can specify amount with float point (ex. 40000.5) + newbie-amount: 100 + # Additional premium euro currency, may be only for donaters.. hmm.. :) + euro: + symbol: '€' + max-amount: 400000 + # newbie-amount parameter is not specified and will be 0 (disabled) by default diff --git a/src/main/resources/locales/messages_en.yml b/src/main/resources/locales/messages_en.yml new file mode 100644 index 0000000..49696dd --- /dev/null +++ b/src/main/resources/locales/messages_en.yml @@ -0,0 +1,164 @@ +# ------------------------------------------------------------------ +# +# ${project.name} messages file for english localization +# Generated by version ${project.version} +# +# Author and developer: SoKnight +# +# Github wiki: https://github.com/SoKnight/${project.name}/wiki +# SpigotMC page: https://www.spigotmc.org/resources/peconomy.73827/ +# +# ------------------------------------------------------------------ +# +# All messages can be colored, use color codes as '&' symbol and color index after it. +# Some messages has placeholders; it is words which allocated with '%' symbols: '%player%', '%currency%' and other. +# Please, don't edit this placeholders because they will be replaced to real data and you can miss it. +# Also don't edit messages sections structure, it's very important for internal code of plugin. +# +# If you see notification in game chat about an undiscovered message, this messages file is outdated and you must regenerate it; +# just delete file messages_en.yml in this plugin folder and reload plugin (using /peco reload) or restart server fully +# +# +# Frequent errors +error: + no-args: "&cSpecify subcommand or use '/peco help' for help." + unknown-subcommand: "&cUnknown subcommand, you can use '/peco help' for help." + wrong-syntax: "&cWrong syntax of command, you can use '/peco help' for help." + only-for-players: "&cOnly players can use this command." + no-permissions: "&cYou don't have permission for this command." + arg-is-not-float: "&cArgument '%argument%' must be positive integer or float." + arg-is-not-int: "&cArgument '%argument%' must be positive integer." + unknown-currency: "&cUnknown currency '%currency%'." + unknown-wallet: "&cPlayer '%player%' has not wallet yet." + +# Operations which used in other messages +operation: + increase: "&a»" + decrease: "&c»" + +# Balance add +add: + limit: "&cThere are %amount%%currency% already on %player%'s balance and after adding his balance will reach the limit which is %limit%%currency%." + other: "&fAdded &b%amount%%currency% &fto &b%player%'s &fbalance: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + self: "&6| \n&6| &fYou received &b%amount%%currency% &fto your balance: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)\n&6| " + +# Balance set +set: + limit: "&cThis transaction will reach the limit which is %limit%%currency%." + already: "&c%player%'s balance for this currency already equals %amount%%currency%." + other: "&fChanged &b%player%'s &fbalance: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + self: "&6| \n&6| &fYour balance changed: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)\n&6| " + +# Balance reset +reset: + already: "&c%player%'s balance for this currency is empty already." + other: "&fNullified &b%player%'s &fbalance: &b%from%%currency% %operation% &b0%currency% &7(#%id%)" + self: "&6| \n&6| &fYour balance has been nullified: &b%from%%currency% %operation% &b0%currency% &7(#%id%)\n&6| " + +# Balance take +take: + not-enough: "&cThere are only %amount%%currency% on %player%'s balance that is less than requested %requested%%currency%." + other: "&fTaked &b%amount%%currency% &ffrom &b%player%'s &fbalance: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + self: "&6| \n&6| &fTaked &b%amount%%currency% &ffrom your balance: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)\n&6| " + +# Balance info +balance: + # 'format' and 'separator' is a backend values which will be used for formatting value for %balance% placeholder + # 'separator' separates currencies amounts if there are several of them + # By default plugin will display empty currencies wallets, but you can disable it in config.yml + format: "&b%amount%%currency%" + separator: "&f, " + # No children permissions + other-balance: "&cYou don't have permissions to see balances of other players." + offline-balance: "&cYou don't have permissions to see balances of offline players." + # Empty wallet + empty-other: "&cWallet of %player% is empty :(" + empty-self: "&cYour wallet is empty :(" + # Successfully executed + other: "&fWallet balance of %player%: &r%balance%" + self: "&fYour wallet balance: &r%balance%" + +# Money sharing +pay: + to-self: "&cOh.. It's useless, isn't it?" + offline-pay: "&cYou don't have permissions to transfer money to offline players." + not-enough: "&cThere are only %amount%%currency% on your balance that is less than requested %requested%%currency%." + limit: "&cThis transaction will reach the limit (%limit%%currency%) for payment receiver." + other: "&fSent &b%amount%%currency% &fto &b%player%'s &fwallet, your balance changed: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + self: "&6| \n&6| &fYou received &b%amount%%currency% &ffrom &b%source%&f: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + +# Transactions history +history: + # No children permissions + other-history: "&cYou don't have permissions to see transactions history of other players." + offline-history: "&cYou don't have permissions to see transactions history of offline players." + # Empty output + empty-self: "&cYour history is empty." + empty-other: "&cHistory for %player%'s wallet is empty." + empty-page: "&cPage %page% is empty." + # Successed + header-other: " &7Transactions history for %player% [%page%/%total%]" + header-self: " &7Your transactions history [%page%/%total%]" + body: " &7#%id% &f%date%: &b%from%%currency% %operation% &b%to%%currency% &7(%action%)" + footer-other: " " + footer-self: " " + +# Transaction info +info: + not-found: "&cTransaction #%id% is not exist." + # No children permissions + other-info: "&cYou don't have permissions to see transaction info of other player." + offline-info: "&cYou don't have permissions to see transaction info of offline player." + # Successed + header: " &7Information about transaction" + footer: " " + # You can change sequence of messages and remove useless + list: + id: " &fUnique ID: &b#%id%" + owner: " &fWallet owner: &b%owner%" + action: " &fAction: &b%action%" + pre: " &fPre-transaction balance: &b%pre%%symbol%" + post: " &fPost-transaction balance: &b%post%%symbol%" + currency: " &fWallet currency: &b%currency% (%symbol%)" + date: " &fDate: &b%date%" + +# Actions for %action% placeholder +# You can use placeholder %source% to display source of action (other player, sender of command) +# * Source displaying will display all players (and admins, which may be wanted silent transaction) +action: + add: "Added by #staff" + set: "Changed by #staff" + reset: "Nullified by #staff" + take: "Taked by #staff" + pay: "Payment from %source%" + failed: "&cFailed" + +# Just plugin reloaded message (for /peco reload) +reload-success: "&fPEconomy reloaded." + +# Help messages +help: + header: " &7Help for PEconomy" + body: " &b/%command% &f&l- &f%description%" + footer: " " + # Descriptions for commands + descriptions: + help: 'Display help page' + balance: 'View balance of all wallets' + pay: 'Transfer money to other player' + history: 'View transactions history' + info: 'Display transaction info' + add: 'Add amount to balance' + set: 'Change balance value' + reset: 'Nullify the balance' + take: 'Take amount from balance' + reload: 'Reload configurations' + # Placeholders for commands + # Will be used after subcommands if it is needed + placeholders: + player: '' + player-optional: '(player)' + currency: '' + amount: '' + page: '(page)' + id: "" \ No newline at end of file diff --git a/src/main/resources/locales/messages_ru.yml b/src/main/resources/locales/messages_ru.yml new file mode 100644 index 0000000..380c56f --- /dev/null +++ b/src/main/resources/locales/messages_ru.yml @@ -0,0 +1,162 @@ +# ------------------------------------------------------------------ +# +# Файл сообщений ${project.name} для русской локализации +# Сгенерирован версией плагина ${project.version} +# +# Автор и разработчик: SoKnight +# +# ВиКи на Github: https://github.com/SoKnight/${project.name}/wiki +# Страница на SpigotMC: https://www.spigotmc.org/resources/peconomy.73827/ +# +# ------------------------------------------------------------------ +# +# Все сообщения могут быть цветными, используйте цветовые коды в виде символа '&' и индекса цвета (0-f) после него. +# Некоторые сообщения имеют подстановки; это слова, которые окружены символами '%', например: '%player%', '%currency%' и др. +# Пожалуйста, не изменяйте эти подстановки, поскольку они будут заменены на реальные данные, которые вы можете упустить. +# Также, не изменяйте структуру секций с сообщениями, ибо это очень важно для внутреннего кода плагина. +# +# Если вы видите уведомление о том, что какое-то сообщение не найдено, значит этот файл устарел и вам необходимо его перегенерировать; +# просто удалите messages_ru.yml в папке плагина и перезагрузите плагин (используя /peco reload) или перезапустите сервер полностью. +# +# +# Часто встречаемые ошибки +error: + no-args: "&cУкажите команду или используйте '/peco help' для помощи." + unknown-subcommand: "&cНеизвестная команда, вы можете посмотреть '/peco help' для помощи." + wrong-syntax: "&cНеверное использование команды, вы можете посмотреть '/peco help' для помощи." + only-for-players: "&cТолько игроки могут использовать эту команду." + no-permissions: "&cНедостаточно прав." + arg-is-not-float: "&cАргумент '%argument%' должен быть положительным целочисленным или дробным числом." + unknown-currency: "&cНеизвестная валюта '%currency%'." + unknown-wallet: "&cИгрок '%player%' не имеет кошелька." + +# Operations which used in other messages +operation: + increase: "&a»" + decrease: "&c»" + +# Balance add +add: + limit: "&cУже есть %amount%%currency% на балансе игрока %player% и после этой операции его баланс привысит лимит в %limit%%currency%." + other: "&fДобавлено &b%amount%%currency% &fна баланс игрока &b%player%&f: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + self: "&6| \n&6| &fВы получили &b%amount%%currency% &fна ваш баланс: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)\n&6| " + +# Balance set +set: + limit: "&cУказанная сумма превышает лимит для этой валюты размером %limit%%currency%." + already: "&cНа балансе игрока %player% уже %amount%%currency%." + other: "&fИзменён баланс игрока &b%player%&f: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + self: "&6| \n&6| &fВаш баланс изменён: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)\n&6| " + +# Balance reset +reset: + already: "&cБаланс игрока %player% для этой валюты итак пустой." + other: "&fАннулирован баланс игрока &b%player%&f: &b%from%%currency% %operation% &b0%currency% &7(#%id%)" + self: "&6| \n&6| &fВаш баланс был аннулирован: &b%from%%currency% %operation% &b0%currency% &7(#%id%)\n&6| " + +# Balance take +take: + not-enough: "&cЕсть только %amount%%currency% на балансе игрока %player%, что меньше запрашиваемых %requested%%currency%." + other: "&fСнято &b%amount%%currency% &fс баланса игрока &b%player%&f: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + self: "&6| \n&6| &fСнято &b%amount%%currency% &fс вашего баланса: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)\n&6| " + +# Balance info +balance: + # 'format' and 'separator' is a backend values which will be used for formatting value for %balance% placeholder + # 'separator' separates currencies amounts if there are several of them + # By default plugin will display empty currencies wallets, but you can disable it in config.yml + format: "&b%amount%%currency%" + separator: "&f, " + # No children permissions + other-balance: "&cНедостаточно прав для просмотра баланса других игроков." + offline-balance: "&cНедостаточно прав для просмотра баланса игроков оффлайн." + # Empty wallet + empty-other: "&cКошелёк %player% пуст :(" + empty-self: "&cВаш кошелёк пуст :(" + # Successfully executed + other: "&fБаланс кошелька игрока %player%: &r%balance%" + self: "&fБаланс вашего кошелька: &r%balance%" + +# Money sharing +pay: + to-self: "&cЭто.. бесполезно.. не так ли?" + offline-pay: "&cНедостаточно прав для перевода средств на счёт игрока оффлайн." + not-enough: "&cЕсть только %amount%%currency% на вашем балансе, что меньше запрашиваемых %requested%%currency%." + limit: "&cЭтот перевод отмёнен, поскольку превышает лимит для этой валюты (%limit%%currency%)." + other: "&fОтправлено &b%amount%%currency% &fна счёт &b%player% &f, баланс: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + self: "&6| \n&6| &fВы получили перевод в &b%amount%%currency% &fот &b%source%&f: &b%from%%currency% %operation% &b%to%%currency% &7(#%id%)" + +# Transactions history +history: + # No children permissions + other-history: "&cНедостаточно прав для просмотра истории транзакций другого игрока." + offline-history: "&cНедостаточно прав для просмотра истории транзакций игрока оффлайн." + # Empty output + empty-self: "&cВаша история транзакций пуста." + empty-other: "&cИстория транзакций кошелька %player% пуста." + empty-page: "&cСтраница истории %page% пуста." + # Successed + header-other: " &7История транзакций для %player%" + header-self: " &7Ваша история транзакций" + body: " &7#%id% &f%date%: &b%from%%currency% %operation% &b%to%%currency% &7(%action%)" + footer-other: " " + footer-self: " " + +# Transaction info +info: + not-found: "&cТранзакция #%id% не найдена." + # No children permissions + other-info: "&cНедостаточно прав для просмотра информации о транзакции другого кошелька." + offline-info: "&cНедостаточно прав для просмотра информации о транзакции кошелька оффлайн игрока." + # Successed + header: " &7Информация о транзакции" + footer: " " + # You can change sequence of messages + list: + id: " &fИдентификатор: &b#%id%" + owner: " &fВладелец кошелька: &b%owner%" + action: " &fДействие: &b%action%" + pre: " &fБаланс до: &b%pre%%symbol%" + post: " &fБаланс после: &b%post%%symbol%" + currency: " &fОперируемая валюта: &b%currency% (%symbol%)" + date: " &fДата: &b%date%" + +# Actions for %action% placeholder +# You can use placeholder %source% to display source of action (other player, sender of command) +# * Source displaying will display all players (and admins, which may be wanted silent transaction) +action: + add: "Добавлено администратором" + set: "Изменено администратором" + reset: "Аннулировано администратором" + take: "Снято администратором" + pay: "Перевод от %source%" + +# Just plugin reloaded message (for /peco reload) +reload-success: "&fPEconomy перезагружен." + +# Help messages +help: + header: " &7Помощь по PEconomy" + body: " &b/%command% &f&l- &f%description%" + footer: " " + # Descriptions for commands + descriptions: + help: 'Показать эту страницу' + balance: 'Показать баланс кошелька' + pay: 'Перевести сумму' + history: 'Показать историю транзакций' + info: 'Показать информацию о транзакции' + add: 'Добавить сумму на счёт' + set: 'Изменить баланс счёта' + reset: 'Аннулировать счёт' + take: 'Снять сумму со счёта' + reload: 'Перезагрузить конфигурацию' + # Placeholders for commands + # Will be used after subcommands if it is needed + placeholders: + player: '<игрок>' + player-optional: '(игрок)' + currency: '<валюта>' + amount: '<сумма>' + page: '(страница)' + id: "<идентификатор>" \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..d369c75 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,107 @@ +name: ${project.name} +main: ru.soknight.peconomy.PEconomy +author: SoKnight +version: ${project.version} +depend: [SKLibrary] +softdepend: [Vault] +api-version: 1.13 +commands: + peco: + aliases: [eco, economy, peco] + description: Main plugin command + usage: /peco help + balance: + aliases: [bal, money] + description: See your or player's balance + usage: /balance (player) + pay: + aliases: [transfer] + description: Transfer money to other wallet + usage: /pay +permissions: + # Allows to see /peco help page + peco.command.help: + default: true + + # Allows to see transactions history (own) + peco.command.history: + default: op + + # Allows to see all history include other online wallet owner + peco.command.history.other: + default: op + children: + peco.command.history: true + + # Allows to see all history include other offline wallet owner + peco.command.history.offline: + default: op + children: + peco.command.history: true + peco.command.history.other: true + + # Allows to see information about transaction (own) + peco.command.info: + default: op + + # Allows to see info about transactions of other online wallet owner + peco.command.info.other: + default: op + children: + peco.command.info: true + + # Allows to see info about transactions of other offline waller owner too + peco.command.info.offline: + default: op + children: + peco.command.info: true + peco.command.info.other: true + + # Allows to use /peco add command + peco.command.add: + default: op + + # Allows to use /peco set command + peco.command.set: + default: op + + # Allows to use /peco reset command + peco.command.reset: + default: op + + # Allows to use /peco take command + peco.command.take: + default: op + + # Allows to use /peco reload command + peco.command.reload: + default: op + + # Allows to see own balance + peco.command.balance: + default: true + + # Allows to see balance of other online wallet owner + peco.command.balance.other: + default: op + children: + peco.command.balance: true + + # Allows to see balance of other offline wallet owner too + peco.command.balance.offline: + default: op + children: + peco.command.balance: true + peco.command.balance.other: true + + # Allows to transfer money to wallet of online player + peco.command.pay: + default: true + + # Allows to transfer money to wallet of offline player too + peco.command.pay.offline: + default: op + + # Allows to see hided by default transaction source + peco.transaction.sourcespy: + default: op \ No newline at end of file