From b3ac8f8614383b20e99105b38c0aa7bfff84b453 Mon Sep 17 00:00:00 2001 From: Olof Larsson Date: Sun, 6 Feb 2011 13:36:11 +0100 Subject: [PATCH] what? --- .gitignore | 19 + LGPL.txt | 165 ++++ LICENCE.txt | 674 +++++++++++++++++ README.md | 7 + packaging/MANIFEST.MF | 3 + packaging/gson-license.txt | 13 + packaging/gson.jar | Bin 0 -> 164986 bytes packaging/plugin.yml | 3 + src/com/bukkit/mcteam/factions/Commands.java | 704 ++++++++++++++++++ src/com/bukkit/mcteam/factions/Factions.java | 72 ++ .../factions/FactionsBlockListener.java | 101 +++ .../factions/FactionsEntityListener.java | 97 +++ .../factions/FactionsPlayerListener.java | 103 +++ .../mcteam/factions/entities/Board.java | 156 ++++ .../bukkit/mcteam/factions/entities/Conf.java | 211 ++++++ .../mcteam/factions/entities/Coord.java | 77 ++ .../bukkit/mcteam/factions/entities/EM.java | 316 ++++++++ .../mcteam/factions/entities/Faction.java | 363 +++++++++ .../mcteam/factions/entities/Follower.java | 400 ++++++++++ .../mcteam/factions/struct/Relation.java | 48 ++ .../bukkit/mcteam/factions/struct/Role.java | 20 + src/com/bukkit/mcteam/factions/util/Log.java | 39 + .../bukkit/mcteam/factions/util/TextUtil.java | 63 ++ src/com/bukkit/mcteam/util/AsciiCompass.java | 89 +++ src/com/bukkit/mcteam/util/ChatFixUtil.java | 230 ++++++ src/com/bukkit/mcteam/util/DiscUtil.java | 32 + .../google/gson/MapAsArrayTypeAdapter.java | 168 +++++ 27 files changed, 4173 insertions(+) create mode 100644 .gitignore create mode 100644 LGPL.txt create mode 100644 LICENCE.txt create mode 100644 README.md create mode 100644 packaging/MANIFEST.MF create mode 100644 packaging/gson-license.txt create mode 100644 packaging/gson.jar create mode 100644 packaging/plugin.yml create mode 100644 src/com/bukkit/mcteam/factions/Commands.java create mode 100644 src/com/bukkit/mcteam/factions/Factions.java create mode 100644 src/com/bukkit/mcteam/factions/FactionsBlockListener.java create mode 100644 src/com/bukkit/mcteam/factions/FactionsEntityListener.java create mode 100644 src/com/bukkit/mcteam/factions/FactionsPlayerListener.java create mode 100644 src/com/bukkit/mcteam/factions/entities/Board.java create mode 100644 src/com/bukkit/mcteam/factions/entities/Conf.java create mode 100644 src/com/bukkit/mcteam/factions/entities/Coord.java create mode 100644 src/com/bukkit/mcteam/factions/entities/EM.java create mode 100644 src/com/bukkit/mcteam/factions/entities/Faction.java create mode 100644 src/com/bukkit/mcteam/factions/entities/Follower.java create mode 100644 src/com/bukkit/mcteam/factions/struct/Relation.java create mode 100644 src/com/bukkit/mcteam/factions/struct/Role.java create mode 100644 src/com/bukkit/mcteam/factions/util/Log.java create mode 100644 src/com/bukkit/mcteam/factions/util/TextUtil.java create mode 100644 src/com/bukkit/mcteam/util/AsciiCompass.java create mode 100644 src/com/bukkit/mcteam/util/ChatFixUtil.java create mode 100644 src/com/bukkit/mcteam/util/DiscUtil.java create mode 100644 src/com/google/gson/MapAsArrayTypeAdapter.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c55a2731 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Eclipse stuff +/.classpath +/.project +/.settings + +# netbeans +/nbproject + +# vim +.*.sw[a-p] + +# various other potential build files +/build +/bin +/dist +/manifest.mf + +# Mac filesystem dust +/.DS_Store \ No newline at end of file diff --git a/LGPL.txt b/LGPL.txt new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/LGPL.txt @@ -0,0 +1,165 @@ + GNU LESSER 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. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/LICENCE.txt b/LICENCE.txt new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/LICENCE.txt @@ -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 00000000..3fbcabd0 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +Factions +==================== +TODO write this + +Compiling +--------- +TODO write this \ No newline at end of file diff --git a/packaging/MANIFEST.MF b/packaging/MANIFEST.MF new file mode 100644 index 00000000..5c137bda --- /dev/null +++ b/packaging/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: ../gson.jar + diff --git a/packaging/gson-license.txt b/packaging/gson-license.txt new file mode 100644 index 00000000..634d1aaf --- /dev/null +++ b/packaging/gson-license.txt @@ -0,0 +1,13 @@ +Copyright (c) 2008-2009 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packaging/gson.jar b/packaging/gson.jar new file mode 100644 index 0000000000000000000000000000000000000000..4f797054184ee7096b71bb9c2b8639a2096a2e7c GIT binary patch literal 164986 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%0Dy^w%Du!woT(5MPwF802OcvUCM& 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 literal 0 HcmV?d00001 diff --git a/packaging/plugin.yml b/packaging/plugin.yml new file mode 100644 index 00000000..ef5f77d4 --- /dev/null +++ b/packaging/plugin.yml @@ -0,0 +1,3 @@ +name: Factions +version: 1.0 beta +main: com.bukkit.mcteam.factions.Factions \ No newline at end of file diff --git a/src/com/bukkit/mcteam/factions/Commands.java b/src/com/bukkit/mcteam/factions/Commands.java new file mode 100644 index 00000000..9411b6c2 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/Commands.java @@ -0,0 +1,704 @@ +package com.bukkit.mcteam.factions; + +import java.util.*; + +import org.bukkit.ChatColor; + +import com.bukkit.mcteam.factions.entities.*; +import com.bukkit.mcteam.factions.struct.*; +import com.bukkit.mcteam.factions.util.*; + +public class Commands { + public static ArrayList> helpPages; + + //----------------------------------------------// + // Build the help pages + //----------------------------------------------// + + static { + helpPages = new ArrayList>(); + ArrayList pageLines; + + + pageLines = new ArrayList(); + pageLines.add(TextUtil.commandHelp(Conf.aliasHelp, "[page]", "Display this, or the next help page")); + pageLines.add(TextUtil.commandHelp(Conf.aliasList, "", "List all factions")); + pageLines.add(TextUtil.commandHelp(Conf.aliasShow, "*[faction name]", "Show faction information")); // TODO display relations! + pageLines.add(TextUtil.commandHelp(Conf.aliasMap, "*[on|off]", "Show territory map, set optional auto update.")); // TODO COMPASS + pageLines.add(TextUtil.commandHelp(Conf.aliasJoin, "[faction name]", "Join a faction")); + pageLines.add(TextUtil.commandHelp(Conf.aliasLeave, "", "Leave your faction")); + pageLines.add(TextUtil.commandHelp(Conf.aliasCreate, "[faction name]", "Create new faction")); + pageLines.add(TextUtil.commandHelp(Conf.aliasName, "[faction name]", "Rename your faction")); + pageLines.add(TextUtil.commandHelp(Conf.aliasDescription, "[description]", "Set the description for your faction")); + + helpPages.add(pageLines); + pageLines = new ArrayList(); + pageLines.add(TextUtil.commandHelp(Conf.aliasOpen, "", "Switch if invitation is required to join")); + pageLines.add(TextUtil.commandHelp(Conf.aliasTitle, "[player name] *[title]", "Set or remove a players title")); + pageLines.add(TextUtil.commandHelp(Conf.aliasInvite, "[player name]", "Invite player")); + pageLines.add(TextUtil.commandHelp(Conf.aliasDeinvite, "[player name]", "Remove a pending invitation")); + pageLines.add(TextUtil.commandHelp(Conf.aliasKick, "[player name]", "Kick a player from the faction")); + pageLines.add(TextUtil.commandHelp(Conf.aliasModerator, "[player name]", "Give or revoke moderator rights")); // TODO + pageLines.add(TextUtil.commandHelp(Conf.aliasAdmin, "[player name]", "Hand over your admin rights")); // TODO + pageLines.add(TextUtil.commandHelp(Conf.aliasClaim, "", "Claim the land where you are standing")); + pageLines.add(TextUtil.commandHelp(Conf.aliasUnclaim, "", "Unclaim the land where you are standing")); + + helpPages.add(pageLines); + pageLines = new ArrayList(); + + pageLines.add(TextUtil.commandHelp(Conf.aliasRelationAlly, "[faction name]", " ")); + pageLines.add(TextUtil.commandHelp(Conf.aliasRelationNeutral, "[faction name]", " ")); + pageLines.add(TextUtil.commandHelp(Conf.aliasRelationEnemy, "[faction name]", " ")); + pageLines.add(""); + pageLines.add(Conf.colorSystem+"Set which relation your WHISH you had to another faction."); + pageLines.add(Conf.colorSystem+"Per default your relation to another faction will be neutral."); + pageLines.add(""); + pageLines.add(Conf.colorSystem+"If BOTH factions whishes \"ally\" you will be allies."); + pageLines.add(Conf.colorSystem+"If ONE faction whishes \"enemy\" you will be enemies."); + + helpPages.add(pageLines); + pageLines = new ArrayList(); + + pageLines.add(Conf.colorSystem+"You can never hurt members or allies."); + pageLines.add(Conf.colorSystem+"You can not hurt neutrals in their own territory."); + pageLines.add(Conf.colorSystem+"You can always hurt enemies and players without faction."); + pageLines.add(""); + pageLines.add(Conf.colorSystem+"Damage from enemies are reduced in your own territory."); + pageLines.add(Conf.colorSystem+"When you die you loose power. It is restored over time."); + pageLines.add(Conf.colorSystem+"The power of a faction is the sum of all member power."); + pageLines.add(Conf.colorSystem+"The power of a faction determines how much land it can hold."); + pageLines.add(Conf.colorSystem+"You can claim land from a faction if it has to low power."); + + helpPages.add(pageLines); + pageLines = new ArrayList(); + + pageLines.add(Conf.colorSystem+"Only faction members can build and destroy in their own"); + pageLines.add(Conf.colorSystem+"territory. Usage of the following items is also restricted:"); + pageLines.add(Conf.colorSystem+"Door, Chest, Furnace and Dispenser."); + pageLines.add(" "); + pageLines.add(Conf.colorSystem+"Make sure to put pressure plates in front of doors for your"); + pageLines.add(Conf.colorSystem+"guest visitors. Otherwise they can't get through. You can "); + pageLines.add(Conf.colorSystem+"also use this to create member only areas."); + pageLines.add(Conf.colorSystem+"As dispensers are protected you can create traps without"); + pageLines.add(Conf.colorSystem+"worrying about those arrows getting stolen."); + helpPages.add(pageLines); + } + + + //----------------------------------------------// + // Some utils + //----------------------------------------------// + + public static Follower findFollower(Follower me, String name, boolean defaultsToMe) { + if (name.length() == 0 && defaultsToMe) { + return me; + } + + Follower follower = Follower.find(name); + if (follower != null) { + return follower; + } + + me.sendMessage(Conf.colorSystem+"The player \""+name+"\" could not be found"); + return null; + } + + public static Faction findFaction(Follower me, String name, boolean defaultsToMe) { + if (name.length() == 0 && defaultsToMe) { + return me.getFaction(); + } + + // Search player names + Follower follower = Follower.find(name); + if (follower != null) { + return follower.getFaction(); + } + + // Then faction names + Faction faction = Faction.find(name); + if (faction != null) { + return faction; + } + + me.sendMessage(Conf.colorSystem+"No faction or player \""+name+"\" was found"); + return null; + } + + public static boolean canIAdministerYou(Follower i, Follower you) { + if ( ! i.getFaction().equals(you.getFaction())) { + i.sendMessage(you.getFullName(i)+Conf.colorSystem+" is not in the same faction as you."); + return false; + } + + if (i.role.value > you.role.value || i.role.equals(Role.ADMIN) ) { + return true; + } + + if (you.role.equals(Role.ADMIN)) { + i.sendMessage(Conf.colorSystem+"Only the faction admin can do that."); + } else if (i.role.equals(Role.MODERATOR)) { + i.sendMessage(Conf.colorSystem+"Moderators can't controll eachother..."); + } else { + i.sendMessage(Conf.colorSystem+"You must be a faction moderator to do that."); + } + + return false; + } + + //----------------------------------------------// + // The base command + //----------------------------------------------// + + public static void base(Follower me, ArrayList tokens) { + if (tokens.size() == 0) { + help(me); + return; + } + + String command = tokens.get(0).toLowerCase(); + tokens.remove(0); + + if (Conf.aliasHelp.contains(command)) { + int page = 1; + if (tokens.size() > 0) { + page = Integer.parseInt(tokens.get(0)); + } + help(me, page); + } else if (Conf.aliasLeave.contains(command)) { + leave(me); + } else if (Conf.aliasJoin.contains(command)) { + join(me, TextUtil.implode(tokens)); + } else if (Conf.aliasCreate.contains(command)) { + create(me, TextUtil.implode(tokens)); + } else if (Conf.aliasName.contains(command)) { + name(me, TextUtil.implode(tokens)); + } else if (Conf.aliasList.contains(command)) { + list(me); + } else if (Conf.aliasShow.contains(command)) { + showFaction(me, TextUtil.implode(tokens)); + } else if (Conf.aliasMap.contains(command)) { + showMap(me, TextUtil.implode(tokens)); + } else if (Conf.aliasInvite.contains(command)) { + invite(me, TextUtil.implode(tokens)); + } else if (Conf.aliasDeinvite.contains(command)) { + deinvite(me, TextUtil.implode(tokens)); + } else if (Conf.aliasOpen.contains(command)) { + open(me); + } else if (Conf.aliasTitle.contains(command)) { + title(me, tokens); + } else if (Conf.aliasKick.contains(command)) { + kick(me, TextUtil.implode(tokens)); + } else if (Conf.aliasModerator.contains(command)) { + roleChange(me, Role.MODERATOR, TextUtil.implode(tokens)); + } else if (Conf.aliasAdmin.contains(command)) { + roleChange(me, Role.ADMIN, TextUtil.implode(tokens)); + } else if (Conf.aliasClaim.contains(command)) { + claim(me); + } else if (Conf.aliasUnclaim.contains(command)) { + unclaim(me); + } else if (Conf.aliasRelationAlly.contains(command)) { + relation(me, Relation.ALLY, TextUtil.implode(tokens)); + } else if (Conf.aliasRelationNeutral.contains(command)) { + relation(me, Relation.NEUTRAL, TextUtil.implode(tokens)); + } else if (Conf.aliasRelationEnemy.contains(command)) { + relation(me, Relation.ENEMY, TextUtil.implode(tokens)); + } else if (Conf.aliasDescription.contains(command)) { + description(me, TextUtil.implode(tokens)); + } else { + //me.sendMessage(Conf.colorSystem+"Unknown faction command"+Conf.colorCommand+" "+command); + me.sendMessage(Conf.colorSystem+"Unknown faction command"+Conf.colorCommand+" "+command); + //me.getPlayer().sendMessage(TextUtil.repeat(tokens.get(0), Integer.parseInt(tokens.get(1)))); + } + } + + //----------------------------------------------// + // The other commands + //----------------------------------------------// + public static void help(Follower me) { + help(me, 1); + } + + public static void help(Follower me, Integer page) { + me.sendMessage(TextUtil.titleize("Factions Help ("+page+"/"+helpPages.size()+")"), false); + page -= 1; + if (page < 0 || page >= helpPages.size()) { + me.sendMessage(Conf.colorSystem+"That page does not exist"); + return; + } + me.sendMessage(helpPages.get(page), false); + } + + public static void leave(Follower me) { + Faction faction = me.getFaction(); + + ArrayList errors = me.leave(); + me.sendMessage(errors); + + if (errors.size() == 0) { + faction.sendMessage(me.getFullName(faction)+Conf.colorAction+" left your faction."); + me.sendMessage("You left "+faction.getName(me)); + } + + if (faction.getFollowersAll().size() == 0) { + // Remove this faction + for (Follower follower : Follower.getAll()) { + follower.sendMessage(Conf.colorAction+"The faction "+faction.getName(follower)+Conf.colorAction+" was disbandoned."); + } + EM.factionDelete(faction.id); + } + } + + public static void join(Follower me, String name) { + Faction faction = findFaction(me, name, false); + if (faction == null) { + return; + } + + ArrayList errors = me.join(faction); + me.sendMessage(errors); + + if (errors.size() > 0) { + faction.sendMessage(me.getFullName(faction)+Conf.colorSystem+" tried to join your faction."); + } else { + me.sendMessage(Conf.colorAction+"You successfully joined "+faction.getName(me)); + faction.sendMessage(me.getFullName(faction)+Conf.colorAction+" joined your faction."); + } + } + + // TODO MOVE OUT + public static void create(Follower me, String name) { + ArrayList errors = me.createFaction(name); + me.sendMessage(errors); + + if (errors.size() == 0) { + me.sendMessage(Conf.colorAction+"Faction created!"); + } + } + + public static void name(Follower me, String name) { + ArrayList errors = new ArrayList(); + + if (me.factionId == 0) { + errors.add(Conf.colorSystem+"You are not part of any faction"); + } else if (me.role.value < Role.MODERATOR.value) { + errors.add(Conf.colorSystem+"You must be moderator to rename your faction"); + } + + if (Faction.isNameTaken(name) && ! Faction.toComparisonName(name).equals(me.getFaction().getComparisonName())) { + errors.add(Conf.colorSystem+"That name is already taken"); + } + + errors.addAll(Faction.validateName(name)); + + if (errors.size() > 0) { + me.sendMessage(errors); + return; + } + + Faction myFaction = me.getFaction(); + + String oldname = myFaction.getName(); + myFaction.setName(name); + + // Inform + myFaction.sendMessage(me.getFullName(myFaction)+Conf.colorSystem+" changed the name of your faction to "+Conf.colorMember+name); + for (Faction faction : Faction.getAll()) { + if (faction.id == me.factionId) { + continue; + } + faction.sendMessage(Conf.colorSystem+"The faction "+me.getRelationColor(faction)+oldname+Conf.colorSystem+" renamed themselves to "+me.getRelationColor(faction)+name); + } + } + + public static void list(Follower me) { + me.sendMessage(TextUtil.titleize("Faction List"), false); + for (Faction faction : Faction.getAll()) { + me.sendMessage(faction.getName(me)+Conf.colorSystem+" ("+faction.getFollowersWhereOnline(true).size()+" / "+faction.getFollowersAll().size()+" online)"); + } + } + + public static void showFaction(Follower me, String name) { + Faction faction = findFaction(me, name, true); + if (faction == null) { + return; + } + Collection admins = faction.getFollowersWhereRole(Role.ADMIN); + Collection mods = faction.getFollowersWhereRole(Role.MODERATOR); + Collection normals = faction.getFollowersWhereRole(Role.NORMAL); + + me.sendMessage(TextUtil.titleize(faction.getName(me)), false); + me.sendMessage(Conf.colorChrome+"Description: "+Conf.colorSystem+faction.getDescription()); + if (faction.id != 0) { + me.sendMessage(Conf.colorChrome+"Power: "+Conf.colorSystem+faction.getPowerRounded()+" / "+faction.getPowerMaxRounded()); // TODO this is not so easy to understand + me.sendMessage(Conf.colorChrome+"Land: "+Conf.colorSystem+faction.getLandRounded()+" / "+faction.getLandMaxRounded()); + + if(faction.getOpen()) { + me.sendMessage(Conf.colorChrome+"Joining: "+Conf.colorSystem+"no invitation is needed"); + } else { + me.sendMessage(Conf.colorChrome+"Joining: "+Conf.colorSystem+"invitation is required"); + } + } + + String onlineList = Conf.colorChrome+"Members online: "; + String offlineList = Conf.colorChrome+"Members offline: "; + String listpart; + for (Follower follower : admins) { + listpart = follower.getFullName(me)+Conf.colorSystem+", "; + if (follower.isOnline()) { + onlineList += listpart; + } else { + offlineList += listpart; + } + } + for (Follower follower : mods) { + listpart = follower.getFullName(me)+Conf.colorSystem+", "; + if (follower.isOnline()) { + onlineList += listpart; + } else { + offlineList += listpart; + } + } + for (Follower follower : normals) { + listpart = follower.getFullName(me)+Conf.colorSystem+", "; + if (follower.isOnline()) { + onlineList += listpart; + } else { + offlineList += listpart; + } + } + + if (onlineList.endsWith(", ")) { + onlineList = onlineList.substring(0, onlineList.length()-2); + } + if (offlineList.endsWith(", ")) { + offlineList = offlineList.substring(0, offlineList.length()-2); + } + + me.sendMessage(onlineList); + me.sendMessage(offlineList); + } + + + public static void showMap(Follower me, String mapAutoUpdating) { + if (mapAutoUpdating.length() > 0) { + if (Conf.aliasTrue.contains(mapAutoUpdating.toLowerCase())) { + // Turn on + me.setMapAutoUpdating(true); + me.sendMessage(Conf.colorAction + "Map auto update ENABLED."); + } else { + // Turn off + me.setMapAutoUpdating(false); + me.sendMessage(Conf.colorAction + "Map auto update DISABLED."); + } + } else { + me.sendMessage(Board.getMap(me.getFaction(), Coord.from(me), me.getPlayer().getLocation().getYaw()), false); + } + } + + public static void invite(Follower me, String name) { + Follower follower = findFollower(me, name, false); + if (follower == null) { + return; + } + + ArrayList errors = me.invite(follower); + me.sendMessage(errors); + + if (errors.size() == 0) { + ChatColor relationColor = me.getRelationColor(follower); + follower.sendMessage(relationColor+me.getFullName()+Conf.colorSystem+" invited you to "+relationColor+me.getFaction().getName()); + me.getFaction().sendMessage(me.getFullName(me)+Conf.colorSystem+" invited "+follower.getFullName(me)+Conf.colorSystem+" to your faction."); + //me.sendMessage(Conf.colorAction+"You invited "+relationColor+follower.getFullName()+Conf.colorAction+" to "+Relation.MEMBER.getColor()+me.getFaction().getName()); + } + } + + public static void deinvite(Follower me, String name) { // TODO Move out! + Follower follower = findFollower(me, name, false); + if (follower == null) { + return; + } + + ArrayList errors = me.deinvite(follower); + me.sendMessage(errors); + + if (errors.size() == 0) { + follower.sendMessage(me.getFullName(follower)+Conf.colorSystem+" revoked your invitation to "+me.getFaction().getName(follower)); + me.getFaction().sendMessage(me.getFullName(me)+Conf.colorSystem+" revoked "+follower.getFullName(me)+"'s"+Conf.colorSystem+" invitation."); + } + } + + public static void open(Follower me) { + if (me.role.value < Role.MODERATOR.value) { + me.sendMessage(Conf.colorSystem+"You must be moderator to do this"); + return; + } + Faction myFaction = me.getFaction(); + myFaction.setOpen( ! me.getFaction().getOpen()); + + String open = myFaction.getOpen() ? "open" : "closed"; + + // Inform + myFaction.sendMessage(me.getFullName(myFaction)+Conf.colorSystem+" changed the faction to "+open); + for (Faction faction : Faction.getAll()) { + if (faction.id == me.factionId) { + continue; + } + faction.sendMessage(Conf.colorSystem+"The faction "+myFaction.getName(faction)+Conf.colorSystem+" is now "+open); + } + } + + public static void title(Follower me, ArrayList tokens) { + if (tokens.size() == 0) { + me.sendMessage(Conf.colorSystem+"You must specify a player name"); + return; + } + + String name = tokens.get(0); + tokens.remove(0); + + Follower you = findFollower(me, name, true); + if (you == null) { + return; + } + + if ( ! canIAdministerYou(me, you)) { + return; + } + + // All ok! Set the title! + String title = TextUtil.implode(tokens); + you.setTitle(title); + + // Inform + Faction myFaction = me.getFaction(); + myFaction.sendMessage(me.getFullName(myFaction)+Conf.colorSystem+" changed a title: "+you.getFullName(myFaction)); + } + + public static void kick(Follower me, String name) { + if (name.length() == 0) { + me.sendMessage(Conf.colorSystem+"You must specify a player name."); + return; + } + + Follower you = findFollower(me, name, false); + if (you == null) { + return; + } + + ArrayList errors = me.kick(you); + me.sendMessage(errors); + + if (errors.size() == 0) { + Faction myFaction = me.getFaction(); + myFaction.sendMessage(me.getFullName(myFaction)+Conf.colorSystem+" kicked "+you.getFullName(myFaction)+Conf.colorSystem+" from the faction! :O"); + you.sendMessage(me.getFullName(you)+Conf.colorSystem+" kicked you from "+myFaction.getName(you)+Conf.colorSystem+"! :O"); + } + } + + public static void roleChange(Follower me, Role targetRole, String name) { + if (me.role.value < Role.ADMIN.value) { + me.sendMessage(Conf.colorSystem+"You must be faction admin to do this"); + return; + } + + if (name.length() == 0) { + me.sendMessage(Conf.colorSystem+"You must specify a player name."); + return; + } + + Follower targetFollower = findFollower(me, name, false); + if (targetFollower == null) { + return; + } + + if (targetFollower.factionId != me.factionId) { + ChatColor relationColor = me.getRelationColor(targetFollower); + me.sendMessage(relationColor+targetFollower.getFullName()+Conf.colorSystem+" is not a member in your faction."); + return; + } + + if (targetFollower == me) { + me.sendMessage(Conf.colorSystem+"The target player musn't be yourself."); + return; + } + + if (targetRole == Role.ADMIN) { + me.role = Role.MODERATOR; + targetFollower.role = Role.ADMIN; + + // Inform all players + for (Follower follower : Follower.getAll()) { + if (follower.factionId == me.factionId) { + follower.sendMessage(Conf.colorMember+me.getFullName()+Conf.colorSystem+" gave "+Conf.colorMember+targetFollower.getFullName()+" the leadership of your faction."); + } else { + follower.sendMessage(me.getFullName(follower)+Conf.colorSystem+" gave "+targetFollower.getFullName(follower)+" the leadership of "+me.getFaction().getName(follower)); + } + } + } else if (targetRole == Role.MODERATOR) { + if (targetFollower.role == Role.MODERATOR) { + // Revoke + targetFollower.role = Role.NORMAL; + me.getFaction().sendMessage(Conf.colorMember+targetFollower.getName()+Conf.colorSystem+" is no longer moderator in your faction."); + } else { + // Give + targetFollower.role = Role.MODERATOR; + me.getFaction().sendMessage(Conf.colorMember+targetFollower.getName()+Conf.colorSystem+" was promoted to moderator in your faction."); + } + } + } + + public static void claim(Follower me) { + if (me.factionId == 0) { + me.sendMessage(Conf.colorSystem+"You are not part of any faction."); + return; + } + + Coord coord = Coord.from(me); + Faction otherFaction = coord.getFaction(); + Faction myFaction = me.getFaction(); + + if (myFaction.equals(otherFaction)) { + me.sendMessage(Conf.colorSystem+"You already own this land."); + return; + } + + if (me.role.value < Role.MODERATOR.value) { + me.sendMessage(Conf.colorSystem+"You must be moderator to claim land."); + return; + } + + if (myFaction.getLandRounded() >= myFaction.getLandMaxRounded()) { + me.sendMessage(Conf.colorSystem+"You can't claim more land! You need more power!"); + return; + } + + if (otherFaction.getRelation(me) == Relation.ALLY) { + me.sendMessage(Conf.colorSystem+"You can't claim the land of your allies."); + return; + } + + if (otherFaction.id != 0) { + if ( ! otherFaction.hasLandInflation()) { // TODO more messages + me.sendMessage(me.getRelationColor(otherFaction)+otherFaction.getName()+Conf.colorSystem+" owns this land and are strong enough to keep it."); + return; + } + + if ( ! Board.isBorderCoord(coord)) { + me.sendMessage(Conf.colorSystem+"You must start claiming land at the border of the territory."); + return; + } + } + + if (otherFaction.id == 0) { + myFaction.sendMessage(Conf.colorMember+me.getFullName()+Conf.colorSystem+" claimed some new land :D"); + } else { + // ASDF claimed some of your land 450 blocks NNW of you. + // ASDf claimed some land from FACTION NAME + ChatColor relcolor = myFaction.getRelationColor(otherFaction); + otherFaction.sendMessage(relcolor+me.getFullName()+Conf.colorSystem+" from "+relcolor+myFaction.getName()+Conf.colorSystem+" stole some of your land :O"); + myFaction.sendMessage(Conf.colorMember+me.getFullName()+Conf.colorSystem+" claimed some land from "+relcolor+otherFaction.getName()); + } + + Board.claim(coord, myFaction); + } + + public static void unclaim(Follower me) { + if (me.factionId == 0) { + me.sendMessage(Conf.colorSystem+"You are not part of any faction"); + return; + } + + if (me.role.value < Role.MODERATOR.value) { + me.sendMessage(Conf.colorSystem+"You must be moderator to unclaim land"); + return; + } + + Coord coord = Coord.from(me.getPlayer()); + + if ( ! me.getFaction().equals(coord.getFaction())) { + me.sendMessage(Conf.colorSystem+"You don't own this land."); + return; + } + + Board.unclaim(coord); + me.sendMessage(Conf.colorAction+"Successfully unclaimed"); + } + + public static void relation(Follower me, Relation whishedRelation, String otherFactionName) { + if (me.factionId == 0) { + me.sendMessage(Conf.colorSystem+"You are not part of any faction."); + return; + } + + if (me.role.value < Role.MODERATOR.value) { + me.sendMessage(Conf.colorSystem+"You must be moderator to set relation to other factions."); + return; + } + + if (otherFactionName.length() == 0) { + me.sendMessage(Conf.colorSystem+"You must specify another faction."); + return; + } + + Faction otherFaction = findFaction(me, otherFactionName, false); + if (otherFaction == null) { + return; + } + + if (otherFaction.id == 0) { + me.sendMessage(Conf.colorSystem+"Nope! You can't :) The default faction is not a real faction."); + return; + } + + if (otherFaction.equals(me.getFaction())) { + me.sendMessage(Conf.colorSystem+"Nope! You can't declare a relation to yourself :)"); + return; + } + + Faction myFaction = me.getFaction(); + myFaction.setRelationWish(otherFaction, whishedRelation); + Relation currentRelation = myFaction.getRelation(otherFaction); + ChatColor currentRelationColor = currentRelation.getColor(); + if (whishedRelation == currentRelation) { + otherFaction.sendMessage(Conf.colorSystem+"Your faction is now "+currentRelationColor+whishedRelation.toString()+Conf.colorSystem+" to "+currentRelationColor+myFaction.getName()); + myFaction.sendMessage(Conf.colorSystem+"Your faction is now "+currentRelationColor+whishedRelation.toString()+Conf.colorSystem+" to "+currentRelationColor+otherFaction.getName()); + } else { + otherFaction.sendMessage(currentRelationColor+myFaction.getName()+Conf.colorSystem+ " whishes to be your "+whishedRelation.getColor()+whishedRelation.toString()); + otherFaction.sendMessage(Conf.colorSystem+"Type "+Conf.colorCommand+Conf.aliasBase.get(0)+" "+whishedRelation+" "+myFaction.getName()+Conf.colorSystem+" to accept."); + myFaction.sendMessage(currentRelationColor+otherFaction.getName()+Conf.colorSystem+ " were informed you wishes to be "+whishedRelation.getColor()+whishedRelation); + } + } + + public static void description(Follower me, String desc) { + if (me.factionId == 0) { + me.sendMessage(Conf.colorSystem+"You are not part of any faction"); + return; + } + + if (me.role.value < Role.MODERATOR.value) { + me.sendMessage(Conf.colorSystem+"You must be moderator to set the description"); + return; + } + + me.getFaction().setDescription(desc); + + me.sendMessage(Conf.colorAction+"The new decription was set :D"); + + // Broadcast the description to everyone + for (Follower follower : EM.followerGetAll()) { + follower.sendMessage(Conf.colorSystem+"The faction "+follower.getRelationColor(me)+me.getFaction().getName()+Conf.colorSystem+" changed their description to:"); + follower.sendMessage(Conf.colorSystem+desc); + } + } + +} + + + + + + + + + + diff --git a/src/com/bukkit/mcteam/factions/Factions.java b/src/com/bukkit/mcteam/factions/Factions.java new file mode 100644 index 00000000..7313ddf1 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/Factions.java @@ -0,0 +1,72 @@ +package com.bukkit.mcteam.factions; + +import java.io.File; + +import org.bukkit.Server; +import org.bukkit.event.Event; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import com.bukkit.mcteam.factions.entities.*; +import com.bukkit.mcteam.factions.util.Log; + +public class Factions extends JavaPlugin { + public static PluginLoader pluginLoader; + public static Server server; + public static PluginDescriptionFile desc; + public static File folder; + public static File plugin; + public static ClassLoader cLoader; + + private final FactionsPlayerListener playerListener = new FactionsPlayerListener(this); + private final FactionsEntityListener entityListener = new FactionsEntityListener(this); + private final FactionsBlockListener blockListener = new FactionsBlockListener(this); + + public Factions(PluginLoader pluginLoader, Server instance, PluginDescriptionFile desc, File folder, File plugin, ClassLoader cLoader) { + super(pluginLoader, instance, desc, folder, plugin, cLoader); + + Factions.pluginLoader = pluginLoader; + Factions.server = instance; + Factions.desc = desc; + Factions.folder = folder; + Factions.plugin = plugin; + Factions.cLoader = cLoader; + + Log.info("=== INIT START ==="); + long timeInitStart = System.currentTimeMillis(); + Log.info("You are running version: "+desc.getVersion()); + + EM.loadAll(); + + // Register events + PluginManager pm = instance.getPluginManager(); + pm.registerEvent(Event.Type.PLAYER_COMMAND, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_JOIN, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_CHAT, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_MOVE, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.PLAYER_QUIT, this.playerListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.ENTITY_DEATH, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.ENTITY_DAMAGEDBY_ENTITY, this.entityListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_DAMAGED, this.blockListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_PLACED, this.blockListener, Event.Priority.Normal, this); + pm.registerEvent(Event.Type.BLOCK_INTERACT, this.blockListener, Event.Priority.Normal, this); + + Log.info("=== INIT DONE (Took "+(System.currentTimeMillis()-timeInitStart)+"ms) ==="); + Log.threshold = Conf.logThreshold; + } + + @Override + public void onDisable() { + // TODO Auto-generated method stub + + } + + @Override + public void onEnable() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/com/bukkit/mcteam/factions/FactionsBlockListener.java b/src/com/bukkit/mcteam/factions/FactionsBlockListener.java new file mode 100644 index 00000000..66b1be6e --- /dev/null +++ b/src/com/bukkit/mcteam/factions/FactionsBlockListener.java @@ -0,0 +1,101 @@ +package com.bukkit.mcteam.factions; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BlockDamageLevel; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.event.block.BlockInteractEvent; +import org.bukkit.event.block.BlockListener; +import org.bukkit.event.block.BlockPlaceEvent; + +import com.bukkit.mcteam.factions.entities.*; +import com.bukkit.mcteam.factions.util.TextUtil; + +public class FactionsBlockListener extends BlockListener { + public Factions plugin; + public FactionsBlockListener(Factions plugin) { + this.plugin = plugin; + } + + @Override + public void onBlockPlace(BlockPlaceEvent event) { + if (event.isCancelled()) { + return; // Alright. lets listen to that. + } + if ( ! this.playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock(), "build")) { + event.setCancelled(true); + } + } + + @Override + public void onBlockDamage(BlockDamageEvent event) { + if (event.isCancelled()) { + return; // Alright. lets listen to that. + } + if (event.getDamageLevel() == BlockDamageLevel.BROKEN && ! this.playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock(), "destroy")) { + event.setCancelled(true); + } + } + + public boolean playerCanBuildDestroyBlock(Player player, Block block, String action) { + Coord coord = Coord.parseCoord(block); + Faction otherFaction = coord.getFaction(); + + if (otherFaction.id == 0) { + return true; // This is no faction territory. You may build or break stuff here. + } + + Follower me = Follower.get(player); + Faction myFaction = me.getFaction(); + + // Cancel if we are not in our own territory + if (myFaction != otherFaction) { + me.sendMessage(Conf.colorSystem+"You can't "+action+" in the territory of "+otherFaction.getName(myFaction)); + otherFaction.sendMessage(me.getFullName(otherFaction)+Conf.colorSystem+" tried to "+action+" "+TextUtil.getMaterialName(block.getType())+" in your territory"); + return false; + } + + return true; + } + + @Override + public void onBlockInteract(BlockInteractEvent event) { + if (event.isCancelled()) { + return; // Alright. lets listen to that. + } + + if ( ! (event.getEntity() instanceof Player)) { + // So far mobs does not interact with the environment :P + return; + } + + Block block = event.getBlock(); + Player player = (Player) event.getEntity(); + + if ( ! canPlayerUseRightclickBlock(player, block)) { + event.setCancelled(true); + } + } + + public boolean canPlayerUseRightclickBlock(Player player, Block block) { + Material material = block.getType(); + + // We only care about some material types. + if ( ! Conf.territoryProtectedMaterials.contains(material)) { + return true; + } + + Follower me = Follower.get(player); + Faction myFaction = me.getFaction(); + Coord blockCoord = Coord.from(block.getLocation()); + Faction otherFaction = blockCoord.getFaction(); + + if (otherFaction.id != 0 && myFaction != otherFaction) { + me.sendMessage(Conf.colorSystem+"You can't use "+TextUtil.getMaterialName(material)+" in the territory of "+otherFaction.getName(myFaction)); + otherFaction.sendMessage(me.getFullName(otherFaction)+Conf.colorSystem+" tried to use "+TextUtil.getMaterialName(material)+" in your territory"); + return false; + } + return true; + } +} diff --git a/src/com/bukkit/mcteam/factions/FactionsEntityListener.java b/src/com/bukkit/mcteam/factions/FactionsEntityListener.java new file mode 100644 index 00000000..986f7e27 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/FactionsEntityListener.java @@ -0,0 +1,97 @@ +package com.bukkit.mcteam.factions; + +import java.text.DecimalFormat; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityListener; + +import com.bukkit.mcteam.factions.entities.Conf; +import com.bukkit.mcteam.factions.entities.Follower; +import com.bukkit.mcteam.factions.struct.Relation; +import com.bukkit.mcteam.factions.util.Log; + +public class FactionsEntityListener extends EntityListener { + public Factions plugin; + public FactionsEntityListener(Factions plugin) { + this.plugin = plugin; + } + + @Override + public void onEntityDeath(EntityDeathEvent event) { + Entity entity = event.getEntity(); + if ( ! (entity instanceof Player)) { + return; + } + + Player player = (Player) entity; + Follower follower = Follower.get(player); + follower.onDeath(); + follower.sendMessage(Conf.colorSystem+"Your power is now "+follower.getPowerRounded()+" / "+follower.getPowerMaxRounded()); + } + /** + * Who can I hurt? + * I can never hurt members or allies. + * I can always hurt enemies. + * I can hurt neutrals as long as they are outside their own territory. + */ + @Override + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (event.isCancelled()) { + return; // Some other plugin decided. Alright then. + } + + Entity entity = event.getEntity(); + if ( ! (entity instanceof Player)) { + return; + } + + Entity damager = event.getDamager(); + if ( ! (damager instanceof Player)) { + return; + } + + Log.debug(((Player)entity).getName()+ " is the defender"); + Log.debug(((Player)damager).getName()+ " is the damager"); + + Follower defender = Follower.get((Player)entity); + Follower attacker = Follower.get((Player)damager); + Relation relation = defender.getRelation(attacker); + + // Players without faction may be hurt anywhere + if (defender.factionId == 0) { + return; + } + + // You can never hurt faction members or allies + if (relation == Relation.MEMBER || relation == Relation.ALLY) { + attacker.sendMessage(Conf.colorSystem+"You can't hurt "+relation.getColor()+defender.getFullName()); + event.setCancelled(true); + return; + } + + // You can not hurt neutrals in their own territory. + if (relation == Relation.NEUTRAL && defender.isInOwnTerritory()) { + attacker.sendMessage(Conf.colorSystem+"You can't hurt "+relation.getColor()+defender.getFullName()+" in their own territory."); + defender.sendMessage(relation.getColor()+attacker.getFullName()+Conf.colorSystem+" tried to hurt you."); + event.setCancelled(true); + return; + } + + // Damage will be dealt. However check if the damage should be reduced. + if (defender.isInOwnTerritory()) { + int damage = event.getDamage(); + int toHeal = (int)Math.round(damage * Conf.territoryShieldFactor); + defender.heal(toHeal); + + // Send message + DecimalFormat formatter = new DecimalFormat("#.#"); + String hearts = formatter.format(toHeal / 2.0); + defender.sendMessage(Conf.colorSystem+"Enemy damage reduced by "+ChatColor.RED+hearts+Conf.colorSystem+" hearts."); + } + + } +} diff --git a/src/com/bukkit/mcteam/factions/FactionsPlayerListener.java b/src/com/bukkit/mcteam/factions/FactionsPlayerListener.java new file mode 100644 index 00000000..63690634 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/FactionsPlayerListener.java @@ -0,0 +1,103 @@ +package com.bukkit.mcteam.factions; + +import java.util.*; + +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.entity.*; +import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.event.player.PlayerListener; +import org.bukkit.event.player.PlayerMoveEvent; + +import com.bukkit.mcteam.factions.entities.*; +import com.bukkit.mcteam.factions.util.*; + +public class FactionsPlayerListener extends PlayerListener{ + public Factions plugin; + public FactionsPlayerListener(Factions plugin) { + this.plugin = plugin; + } + + /** + * If someone says something that starts with the factions base command + * we handle that command. + */ + @Override + public void onPlayerCommand(PlayerChatEvent event) { + Player player = event.getPlayer(); + String msg = event.getMessage(); + + if (handleCommandOrChat(player, msg)) { + event.setCancelled(true); + } + } + + @Override + public void onPlayerChat(PlayerChatEvent event) { + Player player = event.getPlayer(); + String msg = event.getMessage(); + + // Process the command or + if ( ! handleCommandOrChat(player, msg) && Conf.useRelationColoredChat) { + for (Player receiver : Factions.server.getOnlinePlayers()) { + Follower follower = Follower.get(player); + receiver.sendMessage("<"+follower.getFullName(Follower.get(receiver))+ChatColor.WHITE+"> "+msg); + } + } + event.setCancelled(true); + } + + public boolean handleCommandOrChat(Player player, String msg) { + ArrayList tokens = TextUtil.split(msg.trim()); + if (Conf.aliasBase.contains(tokens.get(0))) { + tokens.remove(0); + Follower follower = Follower.get(player); + Commands.base(follower, tokens); + return true; + } + return false; + } + + @Override + public void onPlayerJoin(PlayerEvent event) { + EM.onPlayerLogin(event.getPlayer()); + //Follower.get(event.getPlayer()).sendJoinInfo(); + } + + @Override + public void onPlayerQuit(PlayerEvent event) { + Follower follower = Follower.get(event.getPlayer()); + Log.debug("Saved follower on player quit: "+follower.getFullName()); + follower.save(); // We save the followers on logout in order to save their non autosaved state like power. + EM.onPlayerLogout(event.getPlayer()); // Remove the player link. + } + + @Override + public void onPlayerMove(PlayerMoveEvent event) { + // Did we change coord? + Location from = event.getFrom(); + Location to = event.getTo(); + Coord coordFrom = Coord.from(from); + Coord coordTo = Coord.from(to); + if (coordFrom.equals(coordTo)) { + return; + } + + // Yes we did change coord (: + Follower me = Follower.get(event.getPlayer()); + + if (me.isMapAutoUpdating()) { + me.sendMessage(Board.getMap(me.getFaction(), Coord.from(me), me.getPlayer().getLocation().getYaw()), false); + } else { + // Did we change "host"(faction)? + Faction factionFrom = Board.getFactionAt(coordFrom); + Faction factionTo = Board.getFactionAt(coordTo); + if ( factionFrom != factionTo) { + me.sendFactionHereMessage(); + } + } + } + + +} diff --git a/src/com/bukkit/mcteam/factions/entities/Board.java b/src/com/bukkit/mcteam/factions/entities/Board.java new file mode 100644 index 00000000..46c46c22 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/entities/Board.java @@ -0,0 +1,156 @@ +package com.bukkit.mcteam.factions.entities; + +import java.util.*; +import java.util.Map.Entry; + +import org.bukkit.ChatColor; + +import com.bukkit.mcteam.factions.util.TextUtil; +import com.bukkit.mcteam.util.AsciiCompass; + +//import com.bukkit.mcteam.factions.util.*; + +public class Board { + protected static Map coordFactionIds; + + static { + coordFactionIds = new HashMap(); + } + + public static Faction getFactionAt(Coord coord) { + return Faction.get(getFactionIdAt(coord)); + } + public static int getFactionIdAt(Coord coord) { + Integer factionId = coordFactionIds.get(coord); + if (factionId == null) { + return 0; // No faction + } + return factionId; + } + + public static void unclaim(Coord coord) { + coordFactionIds.remove(coord); + save(); + } + + public static void claim(Coord coord, Faction faction) { + coordFactionIds.put(coord, faction.id); + save(); + } + + public static boolean isBorderCoord(Coord coord) { + Faction faction = Board.getFactionAt(coord); + Coord a = coord.getRelative(1, 0); + Coord b = coord.getRelative(-1, 0); + Coord c = coord.getRelative(0, 1); + Coord d = coord.getRelative(0, -1); + return faction != a.getFaction() && faction != b.getFaction() && faction != c.getFaction() && faction != d.getFaction(); + } + + public static void purgeFaction(Faction faction) { + purgeFaction(faction.id); + } + public static void purgeFaction(int factionId) { + Iterator> iter = coordFactionIds.entrySet().iterator(); + while (iter.hasNext()) { + Entry entry = iter.next(); + if (entry.getValue().equals(factionId)) { + iter.remove(); + } + } + } + + public static int getFactionCoordCount(Faction faction) { + return getFactionCoordCount(faction.id); + } + public static int getFactionCoordCount(int factionId) { + int ret = 0; + for (int thatFactionId : coordFactionIds.values()) { + if(thatFactionId == factionId) { + ret += 1; + } + } + return ret; + } + + //----------------------------------------------// + // Map generation + //----------------------------------------------// + + /** + * The map is relative to a coord and a faction + * north is in the direction of decreasing x + * east is in the direction of decreasing z + */ + public static ArrayList getMap(Faction faction, Coord coord, double inDegrees) { + ArrayList ret = new ArrayList(); + ret.add(TextUtil.titleize("("+coord+") "+coord.getFaction().getName(faction))); + + int halfWidth = Conf.mapWidth / 2; + int halfHeight = Conf.mapHeight / 2; + Coord topLeft = coord.getRelative(-halfHeight, halfWidth); + int width = halfWidth * 2 + 1; + int height = halfHeight * 2 + 1; + + // For each row + for (int dx = 0; dx < height; dx++) { + // Draw and add that row + String row = ""; + for (int dz = 0; dz > -width; dz--) { + if(dz == -(halfWidth) && dx == halfHeight) { + row += ChatColor.AQUA+"+"; + } else { + Coord coordHere = topLeft.getRelative(dx, dz); + Faction factionHere = coordHere.getFaction(); + if (factionHere.id == 0) { + row += ChatColor.GRAY+"-"; + } else { + row += factionHere.getRelation(faction).getColor()+"+"; + } + } + } + ret.add(row); + } + + // Get the compass + ArrayList asciiCompass = AsciiCompass.getAsciiCompass(inDegrees, ChatColor.RED, Conf.colorChrome); + // Pad the compass some + asciiCompass.set(0, asciiCompass.get(0)); + asciiCompass.set(1, asciiCompass.get(1)); + asciiCompass.set(2, asciiCompass.get(2)); + // Add the compass + ret.set(1, asciiCompass.get(0)+ret.get(1).substring(3*3)); + ret.set(2, asciiCompass.get(1)+ret.get(2).substring(3*3)); + ret.set(3, asciiCompass.get(2)+ret.get(3).substring(3*3)); + + return ret; + } + + + //----------------------------------------------// + // Persistance + //----------------------------------------------// + + public static boolean save() { + return EM.boardSave(); + } +} + + + + + + + + + + + + + + + + + + + diff --git a/src/com/bukkit/mcteam/factions/entities/Conf.java b/src/com/bukkit/mcteam/factions/entities/Conf.java new file mode 100644 index 00000000..1d72488c --- /dev/null +++ b/src/com/bukkit/mcteam/factions/entities/Conf.java @@ -0,0 +1,211 @@ +package com.bukkit.mcteam.factions.entities; + +import java.util.*; + +import org.bukkit.*; + +import com.bukkit.mcteam.factions.struct.Relation; + +public class Conf { + public static Integer logThreshold; + public static String prefixAdmin; + public static String prefixMod; + public static int factionNameMinLength; + public static int factionNameMaxLength; + + public static int mapHeight; + public static int mapWidth; + + public static double territoryShieldFactor; + + // Chat control: + public static boolean useRelationColoredChat; // This can interfere with other chat formatting plugins. Test to turn it on or off. + // TODO experiment with displayname feature of bukkit + // TODO test to set format instead of overriding and offer a non colored mut **Title alternative... + + // Colors + public static ChatColor colorMember; + public static ChatColor colorAlly; + public static ChatColor colorNeutral; + public static ChatColor colorEnemy; + + public static ChatColor colorSystem; + public static ChatColor colorAction; + public static ChatColor colorChrome; + public static ChatColor colorCommand; + public static ChatColor colorParameter; + + // Command names / aliases + public static List aliasBase = new ArrayList(); + public static List aliasHelp = new ArrayList(); + + public static List aliasList = new ArrayList(); + public static List aliasShow = new ArrayList(); + + public static List aliasMap = new ArrayList(); + public static List aliasHere = new ArrayList(); + + + public static List aliasJoin = new ArrayList(); + public static List aliasLeave = new ArrayList(); + + public static List aliasCreate = new ArrayList(); + public static List aliasName = new ArrayList(); + + public static List aliasTitle = new ArrayList(); + public static List aliasInvite = new ArrayList(); + public static List aliasDeinvite = new ArrayList(); + public static List aliasOpen = new ArrayList(); + + public static List aliasKick = new ArrayList(); + public static List aliasModerator = new ArrayList(); + public static List aliasAdmin = new ArrayList(); + + public static List aliasClaim = new ArrayList(); + public static List aliasUnclaim = new ArrayList(); + + public static List aliasRelationAlly = new ArrayList(); + public static List aliasRelationNeutral = new ArrayList(); + public static List aliasRelationEnemy = new ArrayList(); + + public static List aliasDescription = new ArrayList(); + + // Value aliases + public static List aliasTrue = new ArrayList(); + + // Power + public static double powerPerLand; + public static double powerPerPlayer; + public static double powerPerMinute; // Default health rate + public static double powerPerDeath; + public static double powerDefaultBonus; + + // Protected blocks + public static List territoryProtectedMaterials = new ArrayList(); + + static { + logThreshold = 10; + prefixAdmin = "**"; + prefixMod = "*"; + factionNameMinLength = 3; + factionNameMaxLength = 40; + + mapHeight = 8; + mapWidth = 49; + + territoryShieldFactor = 0.33; + + useRelationColoredChat = true; + + colorMember = ChatColor.GREEN; + colorAlly = ChatColor.LIGHT_PURPLE; + colorNeutral = ChatColor.WHITE; + colorEnemy = ChatColor.RED; + + colorSystem = ChatColor.YELLOW; + colorAction = ChatColor.LIGHT_PURPLE; + colorChrome = ChatColor.GOLD; + colorCommand = ChatColor.AQUA; + colorParameter = ChatColor.DARK_AQUA; + + aliasBase.add("/f"); + aliasBase.add("f"); + aliasBase.add("/faction"); + aliasBase.add("faction"); + aliasBase.add("/factions"); + aliasBase.add("factions"); + + aliasHelp.add("help"); + aliasHelp.add("h"); + aliasHelp.add("?"); + + aliasList.add("list"); + aliasList.add("ls"); + + aliasShow.add("show"); + aliasShow.add("who"); + + aliasMap.add("map"); + aliasHere.add("here"); + + aliasJoin.add("join"); + + aliasLeave.add("leave"); + + aliasCreate.add("create"); + aliasCreate.add("new"); + + aliasName.add("name"); + aliasName.add("rename"); + + aliasTitle.add("title"); + + aliasInvite.add("invite"); + aliasInvite.add("inv"); + + aliasDeinvite.add("deinvite"); + aliasDeinvite.add("deinv"); + + aliasOpen.add("open"); + aliasOpen.add("close"); + + aliasKick.add("kick"); + + aliasModerator.add("mod"); + + aliasAdmin.add("admin"); + + aliasClaim.add("claim"); + + aliasUnclaim.add("unclaim"); + aliasUnclaim.add("declaim"); + + aliasRelationAlly.add("ally"); + aliasRelationNeutral.add("neutral"); + aliasRelationEnemy.add("enemy"); + + aliasDescription.add("desc"); + + aliasTrue.add("true"); + aliasTrue.add("yes"); + aliasTrue.add("y"); + aliasTrue.add("ok"); + aliasTrue.add("on"); + aliasTrue.add("+"); + + powerPerLand = 1; // 1 power grants one land + powerPerPlayer = 5; // One player has 5 power + powerPerMinute = 0.2; // Default health rate... it takes 5 min to heal one death + powerPerDeath = 1; //A death makes you loose 2 power + powerDefaultBonus = 0; //A faction normally has a power bonus + + territoryProtectedMaterials.add(Material.WOODEN_DOOR); + territoryProtectedMaterials.add(Material.DISPENSER); + territoryProtectedMaterials.add(Material.CHEST); + territoryProtectedMaterials.add(Material.FURNACE); + } + + //----------------------------------------------// + // Color picking and stuff + //----------------------------------------------// + + public static ChatColor relationColor(Relation relation) { + if (relation == Relation.MEMBER) { + return colorMember; + } else if (relation == Relation.ALLY) { + return colorAlly; + } else if (relation == Relation.NEUTRAL) { + return colorNeutral; + } else { //if (relation == FactionRelation.ENEMY) { + return colorEnemy; + } + } + + //----------------------------------------------// + // Persistance + //----------------------------------------------// + + public static boolean save() { + return EM.configSave(); + } +} diff --git a/src/com/bukkit/mcteam/factions/entities/Coord.java b/src/com/bukkit/mcteam/factions/entities/Coord.java new file mode 100644 index 00000000..f5b4b499 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/entities/Coord.java @@ -0,0 +1,77 @@ +package com.bukkit.mcteam.factions.entities; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +public class Coord { + protected static transient int cellSize = 16; + public int x, z; + + public Coord(int x, int z) { + this.x = x; + this.z = z; + } + + // TODO implements cloneable + public Coord(Coord coord) { + this.x = coord.x; + this.z = coord.z; + } + + public Coord() { + // Noarg constructor for google gson. + } + + public Coord getRelative(int dx, int dz) { + return new Coord(this.x + dx, this.z + dz); + } + + public static Coord from(int x, int z) { + return new Coord(x / cellSize - (x < 0 ? 1 : 0), z / cellSize - (z < 0 ? 1 : 0)); + } + + public static Coord from(Player player) { + return from(player.getLocation()); + } + + public static Coord from(Follower follower) { + return from(follower.getPlayer()); + } + + public static Coord from(Location loc) { + return from(loc.getBlockX(), loc.getBlockZ()); + } + + public static Coord parseCoord(Block block) { + return from(block.getX(), block.getZ()); + } + + public Faction getFaction() { + return Board.getFactionAt(this); + } + + @Override + public String toString() { + return this.x + "," + this.z; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + x; + result = 31 * result + z; + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (!(obj instanceof Coord)) + return false; + + Coord o = (Coord) obj; + return this.x == o.x && this.z == o.z; + } +} diff --git a/src/com/bukkit/mcteam/factions/entities/EM.java b/src/com/bukkit/mcteam/factions/entities/EM.java new file mode 100644 index 00000000..2f898a64 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/entities/EM.java @@ -0,0 +1,316 @@ +package com.bukkit.mcteam.factions.entities; + +import java.io.*; +import java.lang.reflect.Modifier; +import java.util.*; + +import org.bukkit.entity.Player; + +import com.bukkit.mcteam.factions.Factions; +import com.bukkit.mcteam.factions.util.*; +import com.bukkit.mcteam.util.DiscUtil; +import com.google.gson.*; + +/** + * This is a entity manager that persists object to json. + * Before using the the EM you should always EM.loadAll(). + * The methods assume that all on disc is loaded into memory. + */ +public class EM { + protected static Map followers = new HashMap(); // Where String is a lowercase playername + protected static Map factions = new HashMap(); // Where Integer is a primary auto increment key + protected static int nextFactionId; + + // hardcoded config + protected final static String ext = ".json"; + protected final static File folderBase = Factions.folder; + protected final static File folderFaction = new File(folderBase, "faction"); + protected final static File folderFollower = new File(folderBase, "follower"); + protected final static File fileConfig = new File(folderBase, "conf"+ext); + protected final static File fileBoard = new File(folderBase, "board"+ext); + + public final static Gson gson = new GsonBuilder() + .setPrettyPrinting() + .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.VOLATILE) + .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter()) // a "must have" adapter for GSON + .create(); + + public static void loadAll() { + configLoad(); + Log.threshold = Conf.logThreshold; + boardLoad(); + followerLoadAll(); + factionLoadAll(); + } + + //----------------------------------------------// + // Config methods (load, save) + //----------------------------------------------// + public static boolean configLoad() { + if (fileConfig.exists()) { + try { + gson.fromJson(DiscUtil.read(fileConfig), Conf.class); + Log.info("Config was loaded from disc"); + return true; + } catch (IOException e) { + e.printStackTrace(); + Log.warn("Failed to load the config"); + return false; + } + } + Log.info("No conf.json found! Creating a new one with the default values"); + //configSave(); // FOR DEBUGGING... + return true; + } + + public static boolean configSave() { + try { + DiscUtil.write(fileConfig, gson.toJson(new Conf())); + Log.debug("Config was saved to disc"); + return true; + } catch (IOException e) { + e.printStackTrace(); + Log.warn("Failed to save the config"); + return false; + } + } + + //----------------------------------------------// + // Board methods (load, save) + //----------------------------------------------// + public static boolean boardLoad() { + if (fileBoard.exists()) { + try { + gson.fromJson(DiscUtil.read(fileBoard), Board.class); + Log.info("Board was loaded from disc"); + return true; + } catch (IOException e) { + e.printStackTrace(); + Log.warn("Failed to load the board"); + return false; + } + } + Log.info("No board.json found! Creating a new one with the default values"); + //boardSave(); // FOR DEBUGGING... + return true; + } + + public static boolean boardSave() { + try { + DiscUtil.write(fileBoard, gson.toJson(new Board())); + Log.debug("Board was saved to disc"); + return true; + } catch (IOException e) { + e.printStackTrace(); + Log.warn("Failed to save the board"); + return false; + } + } + + //----------------------------------------------// + // Follower methods (loadAll, get, save) + //----------------------------------------------// + + /** + * This method will create a follower entity and assign the link to the corresponding player. + */ + public static void onPlayerLogin(Player player) { + Follower follower = followerGet(player); + follower.player = player; + } + + public static void onPlayerLogout(Player player) { + followers.get(player.getName()).player = null; + } + + /** + * This method loads all followers from disc into memory. + */ + public static void followerLoadAll() { + Log.info("Loading all followers from disc..."); + folderFollower.mkdirs(); + + class jsonFileFilter implements FileFilter { + public boolean accept(File file) { + return (file.getName().toLowerCase().endsWith(ext) && file.isFile()); + } + } + + File[] jsonFiles = folderFollower.listFiles(new jsonFileFilter()); + + for (File jsonFile : jsonFiles) { + // Extract the name from the filename. The name is filename minus ".json" + String name = jsonFile.getName(); + name = name.substring(0, name.length() - ext.length()); + try { + Follower follower = gson.fromJson(DiscUtil.read(jsonFile), Follower.class); + follower.id = name; + followers.put(follower.id, follower); + Log.debug("loaded follower "+name); + } catch (Exception e) { + e.printStackTrace(); + Log.warn("failed to load follower "+name); + } + } + } + + public static Collection followerGetAll() { + return followers.values(); + } + + /** + * This method returns the follower object for a player + * A new Follower will be created if the player did not have one + */ + public static Follower followerGet(Player player) { + String key = followerKey(player); + + if (followers.containsKey(key)) { + return followers.get(key); + } + + return followerCreate(player); + } + + public static boolean followerSave(String id) { + Object obj = followers.get(id); + if (obj == null) { + Log.warn("Could not save follower "+id+" as it was not loaded"); + return false; + } + folderFollower.mkdirs(); + File file = new File(folderFollower, id+ext); + try { + DiscUtil.write(file, gson.toJson(obj)); + Log.debug("Saved the follower "+id); + return true; + } catch (IOException e) { + e.printStackTrace(); + Log.warn("Failed to save the follower "+id); + return false; + } + + + } + + protected static String followerKey(Player player) { + return player.getName(); + } + + protected static Follower followerCreate(Player player) { + Log.debug("Creating new follower "+followerKey(player)); + Follower follower = new Follower(); + follower.id = followerKey(player); + followers.put(follower.id, follower); + follower.save(); + return follower; + } + + //----------------------------------------------// + // Faction methods (loadAll, get, create, delete, save) + //----------------------------------------------// + + /** + * This method loads all followers from disc into memory. + */ + public static void factionLoadAll() { + Log.info("Loading all factions from disc..."); + folderFaction.mkdirs(); + + class jsonFileFilter implements FileFilter + { + public boolean accept(File file) { + return (file.getName().toLowerCase().endsWith(ext) && file.isFile()); + } + } + + nextFactionId = 0; + File[] jsonFiles = folderFaction.listFiles(new jsonFileFilter()); + for (File jsonFile : jsonFiles) { + // Extract the name from the filename. The name is filename minus ".json" + String name = jsonFile.getName(); + name = name.substring(0, name.length() - ext.length()); + int id = Integer.parseInt(name); + + // Eventually push next id forward + if (nextFactionId < id) { + nextFactionId = id; + } + + try { + Faction faction = gson.fromJson(DiscUtil.read(jsonFile), Faction.class); + faction.id = id; + factions.put(faction.id, faction); + Log.debug("loaded faction "+id); + } catch (Exception e) { + e.printStackTrace(); + Log.warn("Failed to load faction "+id); + } + } + + nextFactionId += 1; // make it the next id and not the current highest. + + // Make sure the default neutral faction exists + if ( ! factions.containsKey(0)) { + Faction faction = new Faction(); + faction.name = "*No faction*"; + faction.description = "\"The faction for the factionless :P\""; + faction.id = 0; + factions.put(faction.id, faction); + } + } + + public static Faction factionGet(Integer factionId) { + return factions.get(factionId); + } + + public static Collection factionGetAll() { + return factions.values(); + } + + public static Faction factionCreate(){ + Faction faction = new Faction(); + faction.id = nextFactionId; + nextFactionId += 1; + factions.put(faction.id, faction); + Log.debug("created new faction "+faction.id); + faction.save(); + return faction; + } + + public static boolean factionDelete(Integer id) { + // NOTE that this does not do any security checks. + // Follower might get orphaned foreign id's + + // purge from board + Board.purgeFaction(id); + + // Remove the file + File file = new File(folderFaction, id+ext); + file.delete(); + + // Remove the faction + factions.remove(id); + + return true; // TODO + } + + public static boolean factionSave(Integer id) { + Object obj = factions.get(id); + if (obj == null) { + Log.warn("Could not save faction "+id+" as it was not loaded"); + return false; + } + folderFaction.mkdirs(); + File file = new File(folderFaction, id+ext); + try { + DiscUtil.write(file, gson.toJson(obj)); + Log.debug("saved the faction "+id); + return true; + } catch (IOException e) { + e.printStackTrace(); + Log.warn("failed to save the faction "+id); + return false; + } + } +} diff --git a/src/com/bukkit/mcteam/factions/entities/Faction.java b/src/com/bukkit/mcteam/factions/entities/Faction.java new file mode 100644 index 00000000..52726c75 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/entities/Faction.java @@ -0,0 +1,363 @@ +package com.bukkit.mcteam.factions.entities; + +import java.util.*; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import com.bukkit.mcteam.factions.Factions; +import com.bukkit.mcteam.factions.struct.Relation; +import com.bukkit.mcteam.factions.struct.Role; +import com.bukkit.mcteam.factions.util.Log; +import com.bukkit.mcteam.util.ChatFixUtil; + +public class Faction { + + public transient int id; + protected Map relationWish; + protected Set invites; // Where string is a follower id (lower case name) + protected boolean open; + protected String name; + protected String description; + + public Faction() { + this.relationWish = new HashMap(); + this.invites = new HashSet(); + this.open = true; + this.name = "Untitled Faction :("; + this.description = "Default faction description :("; + } + + // ------------------------------- + // Information + // ------------------------------- + public String getName() { + return this.getName(""); + } + public String getName(String prefix) { + return prefix+this.name; + } + public String getName(Faction otherFaction) { + return this.getName(otherFaction.getRelationColor(this).toString()); + } + + public String getName(Follower otherFollower) { + return this.getName(otherFollower.getRelationColor(this).toString()); + } + + public void setName(String newName) { + this.name = newName; + this.save(); + } + + public String getDescription() { + return this.description; + } + + public void setDescription(String value) { + this.description = value; + this.save(); + } + + public boolean getOpen() { + return open; + } + + public void setOpen(boolean isOpen) { + open = isOpen; + this.save(); + } + + //----------------------------------------------// + // Power + //----------------------------------------------// + public double getPower() { + double ret = this.getPowerBonus(); + for (Follower follower : this.getFollowersAll()) { + ret += follower.getPower(); + } + return ret; + } + + public double getPowerBonus() { + return Conf.powerDefaultBonus; // TODO this could be modified by commands later on + } + + public double getPowerMax() { + double ret = this.getPowerBonus(); + for (Follower follower : this.getFollowersAll()) { + ret += follower.getPowerMax(); + } + return ret; + } + + public int getPowerRounded() { + return (int) Math.round(this.getPower()); + } + + public int getPowerMaxRounded() { + return (int) Math.round(this.getPowerMax()); + } + + public int getLandRounded() { + return Board.getFactionCoordCount(this); + } + + public double getLandMax() { + return this.getPower() / Conf.powerPerLand; + } + + public int getLandMaxRounded() { + return (int) Math.round(this.getLandMax()); + } + + public boolean hasLandInflation() { + return Board.getFactionCoordCount(this) > this.getLandMaxRounded(); + } + + // ------------------------------- + // Membership management + // ------------------------------- + + + public ArrayList invite(Follower follower) { + ArrayList errors = new ArrayList(); + + Log.debug("follower.getFaction().id"+follower.getFaction().id); + Log.debug("this.id"+this.id); + + if (follower.getFaction().equals(this)) { // error här? + errors.add(Conf.colorSystem+follower.getFullName()+" is already a member of "+this.getName()); + } + + if(errors.size() > 0) { + return errors; + } + + this.invites.add(follower.id); + this.save(); + return errors; + } + + public ArrayList deinvite(Follower follower) { + ArrayList errors = new ArrayList(); + + if (follower.getFaction().equals(this)) { + errors.add(Conf.colorSystem+follower.getFullName()+" is already a member of "+this.getName()); + errors.add(Conf.colorSystem+"You might want to "+Conf.colorCommand+Conf.aliasBase.get(0)+" "+Conf.aliasKick.get(0)+Conf.colorParameter+" "+follower.getName()); + } + + if(errors.size() > 0) { + return errors; + } + + this.invites.remove(follower.id); + this.save(); + return errors; + } + + public ArrayList kick(Follower follower) { + ArrayList errors = new ArrayList(); + removeFollower(follower); + return errors; + } + + + public boolean isInvited(Follower follower) { + return invites.contains(follower.id); + } + + // ------------------------------- + // Followers + // ------------------------------- + + public ArrayList getFollowersAll() { + ArrayList ret = new ArrayList(); + for (Follower follower : Follower.getAll()) { + if (follower.factionId == this.id) { + ret.add(follower); + } + } + return ret; + } + + public ArrayList getFollowersWhereOnline(boolean online) { + ArrayList ret = new ArrayList(); + for (Follower follower : Follower.getAll()) { + if (follower.factionId == this.id && follower.isOnline() == online) { + ret.add(follower); + } + } + return ret; + } + + public ArrayList getFollowersWhereRole(Role role) { + ArrayList ret = new ArrayList(); + + for (Follower follower : Follower.getAll()) { + if (follower.factionId == this.id && follower.role.equals(role)) { + ret.add(follower); + } + } + + return ret; + } + + public void removeFollower(Follower follower) { + if (this.id != follower.factionId) { + return; // safety check + } + + this.invites.remove(follower.id); + follower.resetFactionData(); + follower.save(); + this.save(); + } + + public ArrayList getOnlinePlayers() { + ArrayList ret = new ArrayList(); + for (Player player: Factions.server.getOnlinePlayers()) { + Follower follower = Follower.get(player); + if (follower.factionId == this.id) { + ret.add(player); + } + } + return ret; + } + + //----------------------------------------------// + // Faction name + //----------------------------------------------// + + private transient static ArrayList nameWhitelist = new ArrayList(Arrays.asList(new String []{ + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", + "s", "t", "u", "v", "w", "x", "y", "z" + })); + + public static String toComparisonName(String name) { + String ret = ""; + + for (char c : name.toCharArray()) { + if (nameWhitelist.contains(String.valueOf(c))) { + ret += c; + } + } + + return ret.toLowerCase(); + } + + public static ArrayList validateName(String name) { + ArrayList errors = new ArrayList(); + + if(Faction.toComparisonName(name).length() < Conf.factionNameMinLength) { + errors.add(Conf.colorSystem+"That name is to short"); + } + + if(name.length() > Conf.factionNameMaxLength) { + errors.add(Conf.colorSystem+"That name is to long"); + } + + return errors; + } + + public String getComparisonName() { + return Faction.toComparisonName(this.name); + } + + public static Faction find(String name) { + String compName = Faction.toComparisonName(name); + for (Faction faction : Faction.getAll()) { + if (faction.getComparisonName().equals(compName)) { + return faction; + } + } + return null; + } + + public static boolean isNameTaken(String name) { + return Faction.find(name) != null; + } + + //----------------------------------------------// + // Messages - Directly connected to ChatFixUtil + //----------------------------------------------// + public void sendMessage(String message, boolean fix) { + ChatFixUtil.sendMessage(this.getOnlinePlayers(), message, fix); + } + public void sendMessage(List messages, boolean fix) { + ChatFixUtil.sendMessage(this.getOnlinePlayers(), messages, fix); + } + public void sendMessage(String message) { + ChatFixUtil.sendMessage(this.getOnlinePlayers(), message, true); + } + public void sendMessage(List messages) { + ChatFixUtil.sendMessage(this.getOnlinePlayers(), messages, true); + } + + // ------------------------------- + // Relation and relation colors + // ------------------------------- + + public Relation getRelationWish(Faction otherFaction) { + if (this.relationWish.containsKey(otherFaction.id)){ + return this.relationWish.get(otherFaction.id); + } + return Relation.NEUTRAL; + } + + public void setRelationWish(Faction otherFaction, Relation relation) { + if (this.relationWish.containsKey(otherFaction.id) && relation.equals(Relation.NEUTRAL)){ + this.relationWish.remove(otherFaction.id); + return; + } + this.relationWish.put(otherFaction.id, relation); + } + + public Relation getRelation(Faction otherFaction) { + if (otherFaction.id == 0 || this.id == 0) { + return Relation.NEUTRAL; + } + if (otherFaction.equals(this)) { + return Relation.MEMBER; + } + if(this.getRelationWish(otherFaction).value >= otherFaction.getRelationWish(this).value) { + return otherFaction.getRelationWish(this); + } + return this.getRelationWish(otherFaction); + } + + public Relation getRelation(Follower follower) { + return getRelation(follower.getFaction()); + } + + public ChatColor getRelationColor(Faction otherFaction) { + return this.getRelation(otherFaction).getColor(); + } + + public ChatColor getRelationColor(Follower follower) { + return this.getRelation(follower).getColor(); + } + + //----------------------------------------------// + // Persistance and entity management + //----------------------------------------------// + + public static Faction create() { + return EM.factionCreate(); + } + + public static Faction get(Integer factionId) { + return EM.factionGet(factionId); + } + + public static Collection getAll() { + return EM.factionGetAll(); + } + + public boolean save() { + return EM.factionSave(this.id); + } + +} diff --git a/src/com/bukkit/mcteam/factions/entities/Follower.java b/src/com/bukkit/mcteam/factions/entities/Follower.java new file mode 100644 index 00000000..e00ed0be --- /dev/null +++ b/src/com/bukkit/mcteam/factions/entities/Follower.java @@ -0,0 +1,400 @@ +package com.bukkit.mcteam.factions.entities; + +import java.util.*; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import com.bukkit.mcteam.factions.struct.*; +import com.bukkit.mcteam.factions.util.Log; +import com.bukkit.mcteam.util.ChatFixUtil; + +public class Follower { + public transient String id; // The is the name of the player + public transient Player player; // The is the name of the player + + public int factionId; + public Role role; + private String title; + private double power; + private long lastPowerUpdateTime; + private boolean mapAutoUpdating; + + public Follower() { + this.resetFactionData(); + this.power = this.getPowerMax(); + this.lastPowerUpdateTime = System.currentTimeMillis(); + this.mapAutoUpdating = false; + } + + protected void resetFactionData() { + this.factionId = 0; // The default neutral faction + this.role = Role.NORMAL; + this.title = ""; + } + + public Player getPlayer() { + return this.player; + } + + public boolean isOnline() { + return this.getPlayer() != null; + } + + public boolean isMapAutoUpdating() { + return mapAutoUpdating; + } + + public void setMapAutoUpdating(boolean mapAutoUpdating) { + this.mapAutoUpdating = mapAutoUpdating; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + this.save(); + } + + //----------------------------------------------// + // Health + //----------------------------------------------// + public void heal(int amnt) { + Player player = this.getPlayer(); + if (player == null) { + return; + } + player.setHealth(player.getHealth() + amnt); + } + + + //----------------------------------------------// + // Power + //----------------------------------------------// + public double getPower() { + this.updatePower(); + return this.power; + } + + protected void alterPower(double delta) { + this.power += delta; + if (this.power > this.getPowerMax()) { + this.power = this.getPowerMax(); + } else if (this.power < this.getPowerMin()) { + this.power = this.getPowerMin(); + } + Log.debug("Power of "+this.getFullName()+" is now: "+this.power); + } + + public double getPowerMax() { + return Conf.powerPerPlayer; + } + + public double getPowerMin() { + return -Conf.powerPerPlayer; + } + + public int getPowerRounded() { + return (int) Math.round(this.getPower()); + } + + public int getPowerMaxRounded() { + return (int) Math.round(this.getPowerMax()); + } + + public int getPowerMinRounded() { + return (int) Math.round(this.getPowerMin()); + } + + protected void updatePower() { + long now = System.currentTimeMillis(); + long millisPassed = now - this.lastPowerUpdateTime; + this.lastPowerUpdateTime = now; + + int millisPerMinute = 60*1000; + this.alterPower(millisPassed * Conf.powerPerMinute / millisPerMinute); + //this.save(); // This would save to often. So we save this on player quit instead. + } + + public void onDeath() { + this.updatePower(); + this.alterPower(-Conf.powerPerDeath); + } + + //----------------------------------------------// + // Territory + //----------------------------------------------// + public boolean isInOwnTerritory() { + return Board.getFactionAt(this.getCoord()) == this.getFaction(); + } + + public boolean isInOthersTerritory() { + Faction factionHere = Board.getFactionAt(this.getCoord()); + return factionHere.id != 0 && factionHere != this.getFaction(); + } + + public Coord getCoord() { + return Coord.from(this); + } + + public void sendFactionHereMessage() { + Faction factionHere = Board.getFactionAt(this.getCoord()); + String msg = Conf.colorSystem+" ~ "+factionHere.getName(this); + this.sendMessage(msg); + } + + //----------------------------------------------// + // Faction management + //----------------------------------------------// + public Faction getFaction() { + return EM.factionGet(factionId); + } + + public ArrayList join(Faction faction) { + ArrayList errors = new ArrayList(); + if (faction.id == this.factionId) { + errors.add(Conf.colorSystem+"You are already a member of "+faction.getRelationColor(this)+faction.getName()); + } + + if( ! faction.getOpen() && ! faction.isInvited(this)) { + errors.add(Conf.colorSystem+"This guild requires invitation."); + } + + if (this.factionId != 0) { + errors.add(Conf.colorSystem+"You must leave your current faction first."); + } + + if (errors.size() > 0) { + return errors; + } + + this.resetFactionData(); + if(faction.getFollowersAll().size() == 0) { + this.role = Role.ADMIN; + } else { + this.role = Role.NORMAL; + } + this.factionId = faction.id; + faction.deinvite(this); + this.save(); + + return errors; + } + + public ArrayList leave() { + ArrayList errors = new ArrayList(); + if (this.role == Role.ADMIN && this.getFaction().getFollowersAll().size() > 1) { + errors.add(Conf.colorSystem+"You must give the admin role to someone else first."); + } + + if(this.factionId == 0) { + errors.add(Conf.colorSystem+"You are not member of any faction."); + } + + if (errors.size() > 0) { + return errors; + } + + this.resetFactionData(); + this.save(); + + return errors; + } + + public ArrayList createFaction(String name) { + ArrayList errors = new ArrayList(); + + if (this.factionId != 0) { + errors.add(Conf.colorSystem+"You must leave your current faction first."); + } + + if (Faction.isNameTaken(name)) { + errors.add(Conf.colorSystem+"That name is already in use."); + } + + errors.addAll(Faction.validateName(name)); + + if (errors.size() > 0) { + return errors; + } + + Faction faction = EM.factionCreate(); + faction.setName(name); + faction.save(); + this.join(faction); + this.role = Role.ADMIN; + this.save(); + + return errors; + } + + public ArrayList invite(Follower follower) { + ArrayList errors = new ArrayList(); + + //Log.debug("this.role: "+this.role); + //Log.debug("this.role.value: "+this.role.value); + //Log.debug("FactionRole.MODERATOR.value: "+FactionRole.MODERATOR.value); + + if (this.role.value < Role.MODERATOR.value) { + errors.add(Conf.colorSystem+"You must me be a moderator to invite."); + } + + if(errors.size() > 0) { + return errors; + } + + return this.getFaction().invite(follower); + } + + public ArrayList deinvite(Follower follower) { + ArrayList errors = new ArrayList(); + + if (this.role.value < Role.MODERATOR.value) { + errors.add(Conf.colorSystem+"You must me be a moderator to deinvite."); + } + + if(errors.size() > 0) { + return errors; + } + + return follower.getFaction().deinvite(follower); + } + + public ArrayList kick(Follower follower) { + ArrayList errors = new ArrayList(); + + if ( ! follower.getFaction().equals(this.getFaction())) { + errors.add(this.getRelationColor(follower)+follower.getFullName()+Conf.colorSystem+" is not a member of "+Conf.colorMember+this.getFaction().getName()); + } else if (follower.equals(this)) { + errors.add(Conf.colorSystem+"You can not kick yourself."); + errors.add(Conf.colorSystem+"You might want to "+Conf.colorCommand+Conf.aliasBase.get(0)+" "+Conf.aliasLeave.get(0)); + } else if (follower.role.value >= this.role.value) { // TODO add more informative messages. + errors.add(Conf.colorSystem+"Your rank is to low to kick this player."); + } + + if(errors.size() > 0) { + return errors; + } + + return follower.getFaction().kick(follower); + } + + //----------------------------------------------// + // Login info + //----------------------------------------------// + public void sendJoinInfo() { // TODO Missplaced!? + // Do we even whant to use message of the day... + // Perhaps that is up to another plugin... + //this.getPlayer().sendMessage(ChatColor.GREEN + "This is a faction server! Type "+Conf.colorCommand+"/f"+ChatColor.GREEN +" for more info :D"); + } + + //----------------------------------------------// + // Messages - Directly connected to ChatFixUtil + //----------------------------------------------// + public void sendMessage(String message, boolean fix) { + Player player = this.getPlayer(); + ChatFixUtil.sendMessage(player, message, fix); + } + public void sendMessage(List messages, boolean fix) { + Player player = this.getPlayer(); + ChatFixUtil.sendMessage(player, messages, fix); + } + public void sendMessage(String message) { + Player player = this.getPlayer(); + ChatFixUtil.sendMessage(player, message, true); + } + public void sendMessage(List messages) { + Player player = this.getPlayer(); + ChatFixUtil.sendMessage(player, messages, true); + } + + //----------------------------------------------// + // Search + //----------------------------------------------// + public static Follower find(String name) { + for (Follower follower : EM.followerGetAll()) { + if (follower.getName().equalsIgnoreCase(name.trim())) { + return follower; + } + } + + return null; + } + + // ------------------------------- + // Relation and relation colors + // ------------------------------- + + public Relation getRelation(Faction faction) { + return faction.getRelation(this); + } + + public Relation getRelation(Follower follower) { + return this.getFaction().getRelation(follower); + } + + public ChatColor getRelationColor(Faction faction) { + return faction.getRelationColor(this); + } + + public ChatColor getRelationColor(Follower follower) { + return this.getRelation(follower).getColor(); + } + + //----------------------------------------------// + // Display the name of this follower + //----------------------------------------------// + + public String getName() { + return this.id; + } + + public String getFullName() { + return getFullName(""); + } + + public String getFullName(Faction otherFaction) { + return getFullName(otherFaction.getRelationColor(this).toString()); + } + + public String getFullName(Follower otherFollower) { + return getFullName(otherFollower.getRelationColor(this).toString()); + } + + public String getFullName(String prefix) { + String ret = prefix; + if (this.role.equals(Role.ADMIN)) { + ret += Conf.prefixAdmin; + } else if (this.role.equals(Role.MODERATOR)) { + ret += Conf.prefixMod; + } + + if (this.title.length() > 0) { + ret += this.title + " "; + } + + ret += this.getName(); + + return ret; + } + + + + //----------------------------------------------// + // Persistance and entity management + //----------------------------------------------// + + public boolean save() { + return EM.followerSave(this.id); + } + + public static Follower get(Player player) { + return EM.followerGet(player); + } + + public static Collection getAll() { + return EM.followerGetAll(); + } +} \ No newline at end of file diff --git a/src/com/bukkit/mcteam/factions/struct/Relation.java b/src/com/bukkit/mcteam/factions/struct/Relation.java new file mode 100644 index 00000000..ed7fc53b --- /dev/null +++ b/src/com/bukkit/mcteam/factions/struct/Relation.java @@ -0,0 +1,48 @@ +package com.bukkit.mcteam.factions.struct; + +import org.bukkit.ChatColor; + +import com.bukkit.mcteam.factions.entities.*; + +public enum Relation { + MEMBER(3, "member"), + ALLY(2, "ally"), + NEUTRAL(1, "neutral"), + ENEMY(0, "enemy"); + //UNKNOWN(-1, "unknown"); + + public final int value; + public final String nicename; + + private Relation(final int value, final String nicename) { + this.value = value; + this.nicename = nicename; + } + + @Override + public String toString() { + return this.nicename; + } + + public ChatColor getColor() { + return Conf.relationColor(this); + } + + /*public String getChartDot() { + return Conf.chartDot(this); + } + + public static Relation from(String str) { + if (str.equalsIgnoreCase("member")) { + return Relation.MEMBER; + } else if (str.equalsIgnoreCase("ally")) { + return Relation.ALLY; + } else if (str.equalsIgnoreCase("neutral")) { + return Relation.NEUTRAL; + } else if (str.equalsIgnoreCase("enemy")) { + return Relation.ENEMY; + } + + return Relation.UNKNOWN; + }*/ +} diff --git a/src/com/bukkit/mcteam/factions/struct/Role.java b/src/com/bukkit/mcteam/factions/struct/Role.java new file mode 100644 index 00000000..ea307a10 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/struct/Role.java @@ -0,0 +1,20 @@ +package com.bukkit.mcteam.factions.struct; + +public enum Role { + ADMIN(2, "admin"), + MODERATOR(1, "moderator"), + NORMAL(0, "normal player"); + + public final int value; + public final String nicename; + + private Role(final int value, final String nicename) { + this.value = value; + this.nicename = nicename; + } + + @Override + public String toString() { + return this.nicename; + } +} diff --git a/src/com/bukkit/mcteam/factions/util/Log.java b/src/com/bukkit/mcteam/factions/util/Log.java new file mode 100644 index 00000000..95a43bc7 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/util/Log.java @@ -0,0 +1,39 @@ +package com.bukkit.mcteam.factions.util; + +import java.util.*; + +import org.bukkit.entity.Player; + +import com.bukkit.mcteam.factions.Factions; + +public class Log { + public static String prefix = Factions.desc.getName(); + public static ArrayList debuggers = new ArrayList(); + public static int threshold = 10; + + public static void log(int level, String prefix, String msg) { + if (threshold <= level) { + msg = Log.prefix+prefix+msg; + System.out.println(msg); + for(Player debugger : debuggers) { + debugger.sendMessage(msg); + } + } + } + + public static void debug (String msg) { + log(10, " debug: ", msg); + } + + public static void info (String msg) { + log(20, " info: ", msg); + } + + public static void warn (String msg) { + log(30, " warn: ", msg); + } + + public static void severe (String msg) { + log(40, " severe: ", msg); + } +} diff --git a/src/com/bukkit/mcteam/factions/util/TextUtil.java b/src/com/bukkit/mcteam/factions/util/TextUtil.java new file mode 100644 index 00000000..0eba30a6 --- /dev/null +++ b/src/com/bukkit/mcteam/factions/util/TextUtil.java @@ -0,0 +1,63 @@ +package com.bukkit.mcteam.factions.util; +import java.util.*; + +import org.bukkit.Material; + +import com.bukkit.mcteam.factions.entities.*; + +public class TextUtil { + public static String titleize(String str) { + String line = Conf.colorChrome+repeat("_", 60); + String center = ".[ " + Conf.colorSystem + str + Conf.colorChrome + " ]."; + int pivot = line.length() / 2; + int eatLeft = center.length() / 2; + int eatRight = center.length() - eatLeft; + return line.substring(0, pivot - eatLeft) + center + line.substring(pivot + eatRight); + } + + public static String repeat(String s, int times) { + if (times <= 0) return ""; + else return s + repeat(s, times-1); + } + + public static ArrayList split(String str) { + return new ArrayList(Arrays.asList(str.trim().split("\\s+"))); + } + + public static String implode(List list, String glue) { + String ret = ""; + for (int i=0; i list) { + return implode(list, " "); + } + + public static String commandHelp(List aliases, String param, String desc) { + ArrayList parts = new ArrayList(); + parts.add(Conf.colorCommand+Conf.aliasBase.get(0)); + parts.add(TextUtil.implode(aliases, ", ")); + if (param.length() > 0) { + parts.add(Conf.colorParameter+param); + } + if (desc.length() > 0) { + parts.add(Conf.colorSystem+desc); + } + //Log.debug(TextUtil.implode(parts, " ")); + return TextUtil.implode(parts, " "); + } + + public static String getMaterialName(Material material) { + String ret = material.toString(); + ret = ret.replace('_', ' '); + ret = ret.toLowerCase(); + return ret.substring(0, 1).toUpperCase()+ret.substring(1); + } +} + + diff --git a/src/com/bukkit/mcteam/util/AsciiCompass.java b/src/com/bukkit/mcteam/util/AsciiCompass.java new file mode 100644 index 00000000..c7471202 --- /dev/null +++ b/src/com/bukkit/mcteam/util/AsciiCompass.java @@ -0,0 +1,89 @@ +package com.bukkit.mcteam.util; + +import java.util.*; + +import org.bukkit.ChatColor; + +public class AsciiCompass { + public enum Point { + N('N'), + NE('/'), + E('W'), + SE('\\'), + S('S'), + SW('/'), + W('W'), + NW('\\'); + + public final char asciiChar; + + private Point(final char asciiChar) { + this.asciiChar = asciiChar; + } + + @Override + public String toString() { + return String.valueOf(this.asciiChar); + } + + public String toString(boolean isActive, ChatColor colorActive, ChatColor colorDefault) { + return (isActive ? colorActive : colorDefault)+String.valueOf(this.asciiChar); + } + } + + public static AsciiCompass.Point getCompassPointForDirection(double inDegrees) { + double degrees = (inDegrees - 90) % 360 ; + if (degrees < 0) + degrees += 360; + + if (0 <= degrees && degrees < 22.5) + return AsciiCompass.Point.N; + else if (22.5 <= degrees && degrees < 67.5) + return AsciiCompass.Point.NE; + else if (67.5 <= degrees && degrees < 112.5) + return AsciiCompass.Point.E; + else if (112.5 <= degrees && degrees < 157.5) + return AsciiCompass.Point.SE; + else if (157.5 <= degrees && degrees < 202.5) + return AsciiCompass.Point.S; + else if (202.5 <= degrees && degrees < 247.5) + return AsciiCompass.Point.SW; + else if (247.5 <= degrees && degrees < 292.5) + return AsciiCompass.Point.W; + else if (292.5 <= degrees && degrees < 337.5) + return AsciiCompass.Point.NW; + else if (337.5 <= degrees && degrees < 360.0) + return AsciiCompass.Point.N; + else + return null; + } + + public static ArrayList getAsciiCompass(Point point, ChatColor colorActive, ChatColor colorDefault) { + ArrayList ret = new ArrayList(); + String row; + + row = ""; + row += Point.NW.toString(Point.NW == point, colorActive, colorDefault); + row += Point.N.toString(Point.N == point, colorActive, colorDefault); + row += Point.NE.toString(Point.NE == point, colorActive, colorDefault); + ret.add(row); + + row = ""; + row += Point.W.toString(Point.W == point, colorActive, colorDefault); + row += colorDefault+"+"; + row += Point.E.toString(Point.E == point, colorActive, colorDefault); + ret.add(row); + + row = ""; + row += Point.SW.toString(Point.SW == point, colorActive, colorDefault); + row += Point.S.toString(Point.S == point, colorActive, colorDefault); + row += Point.SE.toString(Point.SE == point, colorActive, colorDefault); + ret.add(row); + + return ret; + } + + public static ArrayList getAsciiCompass(double inDegrees, ChatColor colorActive, ChatColor colorDefault) { + return getAsciiCompass(getCompassPointForDirection(inDegrees), colorActive, colorDefault); + } +} diff --git a/src/com/bukkit/mcteam/util/ChatFixUtil.java b/src/com/bukkit/mcteam/util/ChatFixUtil.java new file mode 100644 index 00000000..3345f197 --- /dev/null +++ b/src/com/bukkit/mcteam/util/ChatFixUtil.java @@ -0,0 +1,230 @@ +package com.bukkit.mcteam.util; + +import java.util.*; + +import org.bukkit.entity.Player; + +/** + * The purpose of this tool is twofold: + * 1: Avoid client crashes due to bad color formating. + * 2: Make color continue on word wrapping + * + * In minecraft the degree sign is used as a prefix to another char to create a color. + * For example the code for white is "\u00A7f". + * The "\u00A7" is the unicode notation for the degree sign and the "f" means white. + * + * When does minecraft wrap the text? After how many chars? + * Answer: + * Because the font isn't monospace this differs depending on what you write. + * However we can fit 53 "M" without wrapping and the 54th char would then wrap (be at the beginning of the next line instead) + * As there is no broader char than "M" we can know for sure the minimum line length is 53. + * Note that this means the number of DISPLAYED chars per row is 53. + * A degree sign and the char after will NOT count, as they will not be displayed as chars. + * + * Good to know: Numbers have the same font width as an M. + * + * When does the client crash? + * Answer: + * When a row ends with a degree char and optionally another sign after. + * Another way to say the same: When a line ends with either a broken or valid color notation. + * AND + * The client will ALWAYS crash if the sign after the last displayed char in a row is a degree char. + * A goofy way to explatin it: + * For a line with only "M" and numbers, the fiftyfourth "displayed char" musn't be a degree sign. + * + * WARNING: + * Above is a hypothesis I have created based on what my experiments have shown. + * I am fairly sure it is correct but please help me test it further. + */ +public class ChatFixUtil { + public final static char deg = '\u00A7'; + public final static int lineLength = 53; + + /** + * This method wraps the msg for you at row lengths of 53, + * avoids client crash scenarios and makes the previous color continue on + * the next line. + * + * The upsides with filtering your messages through this method are: + * - No client crashes. + * - Line wrapping with preserved color. + * + * The downsides are: + * - The width of the chat window will not be used to it's fullest. + * For example you can fit more that 53 commas (,) in a chatwindow row + * but the line would break after 53 displayed chars. + * + * Suggested usage: + * NO NEED TO USE the fix method for static help pages in your plugin. + * As the text is static you can make sure there is no client crash yourself + * and be able to use the full line length. + * + * DO USE in cases like where you output colored messages with playernames in your + * plugin. As the player names have different length there is potential for client crash. + */ + public static ArrayList fix(String msg) { + // Make sure the end of msg is good + msg = cleanMsgEnding(msg); + + ArrayList ret = new ArrayList(); + int displen = 0; // The number of displayed chars in row so far. + String row = ""; + String latestColor = null; + + for (int i = 0; i < msg.length(); i++) { + if (displen == lineLength) { + // it is time to start on the next row! + ret.add(row); + displen = 0; + row = ""; + if (latestColor != null) { + row += deg+latestColor; + } + } + char c = msg.charAt(i); + + if (c == deg) { + latestColor = String.valueOf(msg.charAt(i+1)); + row += deg+latestColor; + i++; + } else { + displen += 1; + row += c; + } + } + ret.add(row); + return ret; + } + + public static ArrayList fix(List messages) { + ArrayList ret = new ArrayList(); + for(String message : messages) { + ret.addAll(fix(message)); + } + return ret; + } + + + /** + * Removes the ending chars as long as they are deg or deg+'anychar' or a space + * As I see it we would never want those chars at the end of a msg. + */ + protected static String cleanMsgEnding (String msg) { + + while (msg.length() > 0) { + if (msg.endsWith(String.valueOf(deg)) || msg.endsWith(" ")) { + msg = msg.substring(0, msg.length()-1); + } else if (msg.length() >= 2 && msg.charAt(msg.length() - 2) == deg) { + msg = msg.substring(0, msg.length()-2); + } else { + break; + } + } + return msg; + } + + /** + * This test util assumes line break after 53 displayed chars. + * The fix method above breaks like that so this method should + * be a valid way to test if a message row would crash a client. + */ + public static String thisMsgWouldCrashClient(String str) { + // There would always be crash if we end with deg or deg+'anychar' + if (str.length() >= 1 && str.charAt(str.length() - 1) == deg) { + return "Crash: The str ends with deg."; + } else if (str.length() >= 2 && str.charAt(str.length() - 2) == deg) { + return "Crash: The str ends with deg+'anychar'."; + } + + int displayedChars = 0; + + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == deg && displayedChars == lineLength) { + return "Crash: Deg as fiftyforth \"displayed\" char"; + } else if (c == deg) { + i++; // this and next: they are not displayed... skip them... + } else { + displayedChars += 1; + } + } + return "all ok"; + } + + //----------------------------------------------// + // Methods for effectively sending messages + //----------------------------------------------// + //----------------------------------------------// + // One player + //----------------------------------------------// + public static void sendMessage(Player player, String message, boolean fix) { + if (fix) { + List messages = ChatFixUtil.fix(message); + sendMessage(player, messages, false); + } else { + if (player != null) { + player.sendMessage(message); + } + } + } + public static void sendMessage(Player player, List messages, boolean fix) { + if (fix) { + messages = ChatFixUtil.fix(messages); + } + for (String message : messages) { + sendMessage(player, message, false); + } + } + public static void sendMessage(Player player, String message) { + sendMessage(player, message, true); + } + public static void sendMessage(Player player, List messages) { + sendMessage(player, messages, true); + } + //----------------------------------------------// + // Many Players + //----------------------------------------------// + public static void sendMessage(Collection players, String message, boolean fix) { + if (fix) { + List messages = ChatFixUtil.fix(message); + sendMessage(players, messages, false); + } else { + for (Player player : players) { + sendMessage(player, message, false); + } + } + } + public static void sendMessage(Collection players, List messages, boolean fix) { + if (fix) { + messages = ChatFixUtil.fix(messages); + } + + for (String message : messages) { + sendMessage(players, message, false); + } + } + public static void sendMessage(Collection players, String message) { + sendMessage(players, message, true); + } + public static void sendMessage(Collection players, List messages) { + sendMessage(players, messages, true); + } +} + + + + + + + + + + + + + + + + + + diff --git a/src/com/bukkit/mcteam/util/DiscUtil.java b/src/com/bukkit/mcteam/util/DiscUtil.java new file mode 100644 index 00000000..62088a04 --- /dev/null +++ b/src/com/bukkit/mcteam/util/DiscUtil.java @@ -0,0 +1,32 @@ +package com.bukkit.mcteam.util; + +import java.io.*; + +/** + * Harddisc related methods such as read and write. + */ +public class DiscUtil { + /** + * Convenience function for writing a string to a file. + */ + public static void write(File file, String content) throws IOException { + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, false), "UTF8")); + out.write(content); + out.close(); + } + + /** + * Convenience function for reading a file as a string. + */ + public static String read(File file) throws IOException { + BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); + String ret = new String(new byte[0], "UTF-8"); + + String line; + while ((line = in.readLine()) != null) { + ret += line; + } + + return ret; + } +} diff --git a/src/com/google/gson/MapAsArrayTypeAdapter.java b/src/com/google/gson/MapAsArrayTypeAdapter.java new file mode 100644 index 00000000..80cd6900 --- /dev/null +++ b/src/com/google/gson/MapAsArrayTypeAdapter.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Adapts maps containing complex keys as arrays of map entries. + * + *

Maps as JSON objects

+ * The standard GSON map type adapter converts Java {@link Map Maps} to JSON + * Objects. This requires that map keys can be serialized as strings; this is + * insufficient for some key types. For example, consider a map whose keys are + * points on a grid. The default JSON form encodes reasonably:
   {@code
+ *   Map original = new LinkedHashMap();
+ *   original.put(new Point(5, 6), "a");
+ *   original.put(new Point(8, 8), "b");
+ *   System.out.println(gson.toJson(original, type));
+ * }
+ * The above code prints this JSON object:
   {@code
+ *   {
+ *     "(5,6)": "a",
+ *     "(8,8)": "b"
+ *   }
+ * }
+ * But GSON is unable to deserialize this value because the JSON string name is + * just the {@link Object#toString() toString()} of the map key. Attempting to + * convert the above JSON to an object fails with a parse exception: + *
com.google.gson.JsonParseException: Expecting object found: "(5,6)"
+ *   at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
+ *   at com.google.gson.ObjectNavigator.navigateClassFields
+ *   ...
+ * + *

Maps as JSON arrays

+ * An alternative approach taken by this type adapter is to encode maps as + * arrays of map entries. Each map entry is a two element array containing a key + * and a value. This approach is more flexible because any type can be used as + * the map's key; not just strings. But it's also less portable because the + * receiver of such JSON must be aware of the map entry convention. + * + *

Register this adapter when you are creating your GSON instance. + *

   {@code
+ *   Gson gson = new GsonBuilder()
+ *     .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter())
+ *     .create();
+ * }
+ * This will change the structure of the JSON emitted by the code above. Now we + * get an array. In this case the arrays elements are map entries: + *
   {@code
+ *   [
+ *     [
+ *       {
+ *         "x": 5,
+ *         "y": 6
+ *       },
+ *       "a",
+ *     ],
+ *     [
+ *       {
+ *         "x": 8,
+ *         "y": 8
+ *       },
+ *       "b"
+ *     ]
+ *   ]
+ * }
+ * This format will serialize and deserialize just fine as long as this adapter + * is registered. + * + *

This adapter returns regular JSON objects for maps whose keys are not + * complex. A key is complex if its JSON-serialized form is an array or an + * object. + */ +public final class MapAsArrayTypeAdapter + implements JsonSerializer>, JsonDeserializer> { + + public Map deserialize(JsonElement json, Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { + Map result = new LinkedHashMap(); + Type[] keyAndValueType = typeToTypeArguments(typeOfT); + if (json.isJsonArray()) { + JsonArray array = json.getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + JsonArray entryArray = array.get(i).getAsJsonArray(); + Object k = context.deserialize(entryArray.get(0), keyAndValueType[0]); + Object v = context.deserialize(entryArray.get(1), keyAndValueType[1]); + result.put(k, v); + } + checkSize(array, array.size(), result, result.size()); + } else { + JsonObject object = json.getAsJsonObject(); + for (Map.Entry entry : object.entrySet()) { + Object k = context.deserialize(new JsonPrimitive(entry.getKey()), keyAndValueType[0]); + Object v = context.deserialize(entry.getValue(), keyAndValueType[1]); + result.put(k, v); + } + checkSize(object, object.entrySet().size(), result, result.size()); + } + return result; + } + + public JsonElement serialize(Map src, Type typeOfSrc, JsonSerializationContext context) { + Type[] keyAndValueType = typeToTypeArguments(typeOfSrc); + boolean serializeAsArray = false; + List keysAndValues = new ArrayList(); + for (Map.Entry entry : src.entrySet()) { + JsonElement key = context.serialize(entry.getKey(), keyAndValueType[0]); + serializeAsArray |= key.isJsonObject() || key.isJsonArray(); + keysAndValues.add(key); + keysAndValues.add(context.serialize(entry.getValue(), keyAndValueType[1])); + } + + if (serializeAsArray) { + JsonArray result = new JsonArray(); + for (int i = 0; i < keysAndValues.size(); i+=2) { + JsonArray entryArray = new JsonArray(); + entryArray.add(keysAndValues.get(i)); + entryArray.add(keysAndValues.get(i + 1)); + result.add(entryArray); + } + return result; + } else { + JsonObject result = new JsonObject(); + for (int i = 0; i < keysAndValues.size(); i+=2) { + result.add(keysAndValues.get(i).getAsString(), keysAndValues.get(i + 1)); + } + checkSize(src, src.size(), result, result.entrySet().size()); + return result; + } + } + + private Type[] typeToTypeArguments(Type typeOfT) { + if (typeOfT instanceof ParameterizedType) { + Type[] typeArguments = ((ParameterizedType) typeOfT).getActualTypeArguments(); + if (typeArguments.length != 2) { + throw new IllegalArgumentException("MapAsArrayTypeAdapter cannot handle " + typeOfT); + } + return typeArguments; + } + return new Type[] { Object.class, Object.class }; + } + + private void checkSize(Object input, int inputSize, Object output, int outputSize) { + if (inputSize != outputSize) { + throw new JsonSyntaxException("Input size " + inputSize + " != output size " + outputSize + + " for input " + input + " and output " + output); + } + } +}