commit 16773ead6a2c9a059d2efa176791c31b74bef119 Author: gamerforEA Date: Sun Mar 22 20:38:04 2015 +0300 Initial commit (Forge 1291). diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c1f84a0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +* text eol=lf +*.bat text eol=crlf +*.patch text eol=lf +*.cfg text eol=lf +*.py text eol=lf +*.png binary +*.exe binary +*.dll binary +*.zip binary +*.pyd binary +*.jar binary +*.lzma binary \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56b3125 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +!.gitignore + +# eclipse +/eclipse + +# gradle +build +.gradle + +*.DS_Store diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..92f7dd2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "bukkit"] + path = bukkit + url = https://github.com/gamerforEA/Bukkit.git +[submodule "forge"] + path = forge + url = https://github.com/gamerforEA/MinecraftForge.git + branch = 1.7.10 diff --git a/LGPL.txt b/LGPL.txt new file mode 100644 index 0000000..65c5ca8 --- /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 0000000..94a9ed0 --- /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 0000000..88b1cb5 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +Cauldron +=========== +Note: 1.7.2 repository has been moved to [branch v172](https://github.com/MinecraftPortCentral/Cauldron/tree/v172) + +A Forge/Bukkit/Spigot Minecraft Server + +Compilation +----------- + +We use Gradle to handle our dependencies. + +1. Checkout project. +2. Init submodules : git submodule update --init --recursive -- depth 1 +3. Setup workspace : gradlew setupCauldron +4. Build binaries : gradlew buildPackages +Note: all binaries will be in distributions folder + +Supporting Cauldron +-------- +Click here to [Donate to bloodmc](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YNCKCALNQKFAS) +Click here to [Become a Patreon] (http://www.patreon.com/bloodmc) + +Top Patron Supporters +----------- + +* isiliden +* eonic +* trab +* jamescowens +* SFTMedia +* Shmeeb + + +Profiling +--------- + +We use YourKit as our Java Profiler. + +YourKit is kindly supporting open source projects with its full-featured Java Profiler. +YourKit, LLC is the creator of innovative and intelligent tools for profiling +Java and .NET applications. Take a look at YourKit's leading software products: +* [YourKit Java Profiler](http://www.yourkit.com/java/profiler/index.jsp) +* [YourKit .NET Profiler](http://www.yourkit.com/.net/profiler/index.jsp) + + +Coding and Pull Request Conventions +----------- + +* We generally follow the Sun/Oracle coding standards. +* No tabs; use 4 spaces instead. +* No trailing whitespaces. +* No CRLF line endings, LF only; will be converted automatically by git +* No 80 column limit or 'weird' midstatement newlines. +* The number of commits in a pull request should be kept to a minimum (squish them into one most of the time - use common sense!). +* No merges should be included in pull requests unless the pull request's purpose is a merge. +* Pull requests should be tested (does it compile? AND does it work?) before submission. +* Any major additions should have documentation ready and provided if applicable (this is usually the case). +* Most pull requests should be accompanied by a corresponding GitHub ticket so we can associate commits with GitHub issues (this is primarily for changelog generation on ci.md-5.net). +* Try to follow test driven development where applicable. + +If you make changes to or add upstream classes (net.minecraft, net.minecraftforge, cpw.mods.fml, org.bukkit, org.spigotmc) it is mandatory to: + +* Make a separate commit adding the new net.minecraft classes (commit message: "Added x for diff visibility" or so). +* Then make further commits with your changes. +* Mark your changes with: + * 1 line; add a trailing: `// Cauldron [- Optional reason]` + * 2+ lines; add + * Before: `// Cauldron start [- Optional comment]` + * After: `// Cauldron end` +* Keep the diffs to a minimum (*somewhat* important) + +Tips to get your pull request accepted +----------- +Making sure you follow the above conventions is important, but just the beginning. Follow these tips to better the chances of your pull request being accepted and pulled. + +* Make sure you follow all of our conventions to the letter. +* Make sure your code compiles under Java 6. +* Provide proper JavaDocs where appropriate. +* Provide proper accompanying documentation where appropriate. +* Test your code. +* Make sure to follow coding best practices. +* Provide a test plugin/mod binary and source for us to test your code with. +* Your pull request should link to accompanying pull requests. +* The description of your pull request should provide detailed information on the pull along with justification of the changes where applicable. + +Credits +------- + +* [MCP](http://mcp.ocean-labs.de) - permission to use data to make Cauldron. +* [Forge](http://www.minecraftforge.net) - mod support. +* [CraftBukkit](http://bukkit.org) - plugin support. +* [Spigot](http://www.spigotmc.org) - performance optimizations. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5be9547 --- /dev/null +++ b/build.gradle @@ -0,0 +1,73 @@ +buildscript { + repositories { + mavenCentral() + mavenLocal() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + } +} + +apply plugin: 'maven' +apply plugin: 'cauldron' + +minecraft { + version = '1.7.10' + mcpVersion = '9.05' + mainClass = 'cpw.mods.fml.relauncher.ServerLaunchWrapper' + tweakClass = 'cpw.mods.fml.common.launcher.FMLTweaker' + installerVersion = "1.4" + subprojects { + repositories { + mavenLocal() + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + flatDir { + name "fileRepo" + dirs "repo" + } + } + } + srgExtra "PK: org/bukkit/craftbukkit org/bukkit/craftbukkit/v1_7_R4" +} + +group = 'net.minecraftforge' +ext.mcVersion = "1.7.10" +ext.cauldronVersion = "1" +ext.forgeVersion = "1291" +ext.bukkitVersion = "01" +version = "${mcVersion}-${cauldronVersion}.${forgeVersion}.${bukkitVersion}.0" + +jenkins { + job = 'Cauldron' +} + +launch4j { + jreMinVersion = '1.6.0' +} + +tasks.packageUniversal { + classifier = 'server' +} + +tasks.packageUniversal.manifest { + attributes([ + 'Implementation-Vendor': 'Cauldron', + 'Implementation-Title': 'Cauldron', + 'Implementation-Version': 'git-Cauldron-Reloaded-'+project.version, + 'Forge-Version': '10.13.2.1291', + 'Specification-Vendor': 'Bukkit Team', + 'Specification-Title': 'Bukkit', + 'Specification-Version': '1.7.10-R0.1-SNAPSHOT' + ]) +} \ No newline at end of file diff --git a/eclipse-workspace-dev.zip b/eclipse-workspace-dev.zip new file mode 100644 index 0000000..ee2cc64 Binary files /dev/null and b/eclipse-workspace-dev.zip differ diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..8280e25 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/forge/fml/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..2d56e9d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\forge\fml\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/jsons/1.7.10-dev.json b/jsons/1.7.10-dev.json new file mode 100644 index 0000000..78de829 --- /dev/null +++ b/jsons/1.7.10-dev.json @@ -0,0 +1,298 @@ +{ + "id": "@minecraft_version@-@project@@version@", + "time": "@timestamp@", + "releaseTime": "1960-01-01T00:00:00-0700", + "type": "release", + "minecraftArguments": "--version FML_DEV --tweakClass cpw.mods.fml.common.launcher.FMLTweaker", + "minimumLauncherVersion": 13, + "assets": "1.7.10", + "libraries": [ + { + "name": "net.minecraft:launchwrapper:1.11" + }, + { + "name": "com.google.code.findbugs:jsr305:1.3.9", + "children": ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.ow2.asm:asm-debug-all:5.0.3", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "com.typesafe.akka:akka-actor_2.11:2.3.3", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "com.typesafe:config:1.2.1", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-actors-migration_2.11:1.1.0", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-compiler:2.11.1", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang.plugins:scala-continuations-library_2.11:1.0.2", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang.plugins:scala-continuations-plugin_2.11.1:1.0.2", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-library:2.11.1", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-parser-combinators_2.11:1.0.1", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-reflect:2.11.1", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-swing_2.11:1.0.1", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-xml_2.11:1.0.2", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "net.sf.jopt-simple:jopt-simple:4.5", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "lzma:lzma:0.0.1" + }, + { + "name": "com.mojang:realms:1.3.5" + }, + { + "name": "org.apache.commons:commons-compress:1.8.1" + }, + { + "name": "org.apache.httpcomponents:httpclient:4.3.3" + }, + { + "name": "commons-logging:commons-logging:1.1.3" + }, + { + "name": "org.apache.httpcomponents:httpcore:4.3.2" + }, + { + "name": "java3d:vecmath:1.3.1" + }, + { + "name": "net.sf.trove4j:trove4j:3.0.3" + }, + { + "name": "com.ibm.icu:icu4j-core-mojang:51.2" + }, + { + "name": "com.paulscode:codecjorbis:20101023" + }, + { + "name": "com.paulscode:codecwav:20101023" + }, + { + "name": "com.paulscode:libraryjavasound:20101123" + }, + { + "name": "com.paulscode:librarylwjglopenal:20100824" + }, + { + "name": "com.paulscode:soundsystem:20120107" + }, + { + "name": "io.netty:netty-all:4.0.10.Final" + }, + { + "name": "com.google.guava:guava:16.0" + }, + { + "name": "org.apache.commons:commons-lang3:3.2.1" + }, + { + "name": "commons-io:commons-io:2.4" + }, + { + "name": "commons-codec:commons-codec:1.9" + }, + { + "name": "net.java.jinput:jinput:2.0.5" + }, + { + "name": "net.java.jutils:jutils:1.0.0" + }, + { + "name": "com.google.code.gson:gson:2.2.4" + }, + { + "name": "com.mojang:authlib:1.5.16" + }, + { + "name": "org.apache.logging.log4j:log4j-api:2.0-beta9" + }, + { + "name": "org.apache.logging.log4j:log4j-core:2.0-beta9" + }, + { + "name": "net.sf.jopt-simple:jopt-simple:4.5", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "org.yaml:snakeyaml:1.9", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "commons-lang:commons-lang:2.6", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "org.avaje:ebean:2.7.3", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "jline:jline:2.6", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "net.md-5:SpecialSource:1.7-SNAPSHOT", + "url" : "https://raw.github.com/MinecraftPortCentral/mcpc-mvn-repo/master/snapshots", + "serverreq":true + }, + { + "name": "net.sourceforge.argo:argo:2.25", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "org.fusesource.jansi:jansi:1.8", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "com.googlecode.json-simple:json-simple:1.1", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "org.xerial:sqlite-jdbc:3.7.2", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "mysql:mysql-connector-java:5.1.14", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "javax.persistence:persistence-api:1.0.2", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "org.lwjgl.lwjgl:lwjgl:2.9.1" + }, + { + "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.1" + }, + { + "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.1", + "natives": { + "linux": "natives-linux", + "windows": "natives-windows", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "net.java.jinput:jinput-platform:2.0.5", + "natives": { + "linux": "natives-linux", + "windows": "natives-windows", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "tv.twitch:twitch:5.16" + }, + { + "name": "tv.twitch:twitch-platform:5.16", + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "linux" + } + } + ], + "natives": { + "linux": "natives-linux", + "windows": "natives-windows-${arch}", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "tv.twitch:twitch-external-platform:4.5", + "rules": [ + { + "action": "allow", + "os": { + "name": "windows" + } + } + ], + "natives": { + "windows": "natives-windows-${arch}" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + } + ], + "mainClass": "net.minecraft.launchwrapper.Launch" +} \ No newline at end of file diff --git a/jsons/1.7.10-rel.json b/jsons/1.7.10-rel.json new file mode 100644 index 0000000..d78ca88 --- /dev/null +++ b/jsons/1.7.10-rel.json @@ -0,0 +1,327 @@ +{ +"install": { + "profileName": "@project@", + "target":"@minecraft_version@-@project@@version@", + "path":"@artifact@", + "version":"@project@ @version@", + "filePath":"@universal_jar@", + "welcome":"Welcome to the simple @project@ installer.", + "minecraft":"@minecraft_version@", + "logo":"/big_logo.png", + "hideClient":true +}, +"versionInfo": { + "id": "@minecraft_version@-@project@@version@", + "time": "@timestamp@", + "releaseTime": "1960-01-01T00:00:00-0700", + "type": "release", + "minecraftArguments": "--username ${auth_player_name} --version ${version_name} --gameDir ${game_directory} --assetsDir ${assets_root} --assetIndex ${assets_index_name} --uuid ${auth_uuid} --accessToken ${auth_access_token} --userProperties ${user_properties} --userType ${user_type} --tweakClass cpw.mods.fml.common.launcher.FMLTweaker", + "minimumLauncherVersion": 13, + "assets": "1.7.10", + "libraries": [ + { + "name": "@artifact@", + "url": "http://files.minecraftforge.net/maven/" + }, + { + "name": "net.minecraft:launchwrapper:1.11", + "serverreq":true + }, + { + "name": "org.ow2.asm:asm-all:5.0.3", + "serverreq":true + }, + { + "name": "com.typesafe.akka:akka-actor_2.11:2.3.3", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "ed62e9fc709ca0f2ff1a3220daa8b70a2870078e", "25a86ccfdb6f6dfe08971f4825d0a01be83a6f2e" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "com.typesafe:config:1.2.1", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "f771f71fdae3df231bcd54d5ca2d57f0bf93f467", "7d7bc36df0989d72f2d5d057309675777acc528b" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang:scala-actors-migration_2.11:1.1.0", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "dfa8bc42b181d5b9f1a5dd147f8ae308b893eb6f", "8c9aaeeb68487ca519411a14068e1b4d69739207" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang:scala-compiler:2.11.1", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "56ea2e6c025e0821f28d73ca271218b8dd04926a", "1444992390544ba3780867a13ff696a89d7d1639" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang.plugins:scala-continuations-library_2.11:1.0.2", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "87213338cd5a153a7712cb574c0ddd2edfee0386", "0b4c1bf8d48993f138d6e10c0c144e50acfff581" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang.plugins:scala-continuations-plugin_2.11.1:1.0.2", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "1f7371605d4ba42aa26d3443440c0083c587b4e9", "1ea655dda4504ae0a367327e2340cd3beaee6c73" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang:scala-library:2.11.1", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "0e11da23da3eabab9f4777b9220e60d44c1aab6a", "1e4df76e835201c6eabd43adca89ab11f225f134" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang:scala-parser-combinators_2.11:1.0.1", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "f05d7345bf5a58924f2837c6c1f4d73a938e1ff0", "a1cbbcbde1dcc614f4253ed1aa0b320bc78d8f1d" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang:scala-reflect:2.11.1", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "6580347e61cc7f8e802941e7fde40fa83b8badeb", "91ce0f0be20f4a536321724b4b3bbc6530ddcd88" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang:scala-swing_2.11:1.0.1", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "b1cdd92bd47b1e1837139c1c53020e86bb9112ae", "d77152691dcf5bbdb00529af37aa7d3d887b3e63" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "org.scala-lang:scala-xml_2.11:1.0.2", + "url" : "http://files.minecraftforge.net/maven/", + "checksums" : [ "7a80ec00aec122fba7cd4e0d4cdd87ff7e4cb6d0", "62736b01689d56b6d09a0164b7ef9da2b0b9633d" ], + "serverreq":true, + "clientreq":true + }, + { + "name": "net.sf.jopt-simple:jopt-simple:4.5", + "serverreq":true + }, + { + "name": "lzma:lzma:0.0.1", + "serverreq":true + }, + { + "name": "com.mojang:realms:1.3.5" + }, + { + "name": "org.apache.commons:commons-compress:1.8.1" + }, + { + "name": "org.apache.httpcomponents:httpclient:4.3.3" + }, + { + "name": "commons-logging:commons-logging:1.1.3" + }, + { + "name": "org.apache.httpcomponents:httpcore:4.3.2" + }, + { + "name": "java3d:vecmath:1.3.1" + }, + { + "name": "net.sf.trove4j:trove4j:3.0.3" + }, + { + "name": "com.ibm.icu:icu4j-core-mojang:51.2" + }, + { + "name": "com.paulscode:codecjorbis:20101023" + }, + { + "name": "com.paulscode:codecwav:20101023" + }, + { + "name": "com.paulscode:libraryjavasound:20101123" + }, + { + "name": "com.paulscode:librarylwjglopenal:20100824" + }, + { + "name": "com.paulscode:soundsystem:20120107" + }, + { + "name": "io.netty:netty-all:4.0.10.Final" + }, + { + "name": "com.google.guava:guava:16.0" + }, + { + "name": "org.apache.commons:commons-lang3:3.2.1" + }, + { + "name": "commons-io:commons-io:2.4" + }, + { + "name": "commons-codec:commons-codec:1.9" + }, + { + "name": "net.java.jinput:jinput:2.0.5" + }, + { + "name": "net.java.jutils:jutils:1.0.0" + }, + { + "name": "com.google.code.gson:gson:2.2.4" + }, + { + "name": "com.mojang:authlib:1.5.16" + }, + { + "name": "org.apache.logging.log4j:log4j-api:2.0-beta9" + }, + { + "name": "org.apache.logging.log4j:log4j-core:2.0-beta9" + }, + { + "name": "org.yaml:snakeyaml:1.9", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "commons-lang:commons-lang:2.6", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "org.avaje:ebean:2.7.3", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "jline:jline:2.6", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "net.md-5:SpecialSource:1.7-SNAPSHOT", + "url" : "https://raw.github.com/MinecraftPortCentral/mcpc-mvn-repo/master/snapshots", + "serverreq":true + }, + { + "name": "org.fusesource.jansi:jansi:1.8", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "net.sourceforge.argo:argo:2.25", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "com.googlecode.json-simple:json-simple:1.1", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "org.xerial:sqlite-jdbc:3.7.2", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "mysql:mysql-connector-java:5.1.14", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "javax.persistence:persistence-api:1.0.2", + "url" : "http://repo.maven.apache.org/maven2", + "serverreq":true + }, + { + "name": "org.lwjgl.lwjgl:lwjgl:2.9.1" + }, + { + "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.1" + }, + { + "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.1", + "natives": { + "linux": "natives-linux", + "windows": "natives-windows", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "net.java.jinput:jinput-platform:2.0.5", + "natives": { + "linux": "natives-linux", + "windows": "natives-windows", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "tv.twitch:twitch:5.16" + }, + { + "name": "tv.twitch:twitch-platform:5.16", + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "linux" + } + } + ], + "natives": { + "linux": "natives-linux", + "windows": "natives-windows-${arch}", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "tv.twitch:twitch-external-platform:4.5", + "rules": [ + { + "action": "allow", + "os": { + "name": "windows" + } + } + ], + "natives": { + "windows": "natives-windows-${arch}" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + } + ], + "mainClass": "net.minecraft.launchwrapper.Launch" +} +} \ No newline at end of file diff --git a/jsons/1.7.10.json b/jsons/1.7.10.json new file mode 100644 index 0000000..baa7023 --- /dev/null +++ b/jsons/1.7.10.json @@ -0,0 +1,191 @@ +{ + "id": "@minecraft_version@-@project@@version@", + "time": "@timestamp@", + "releaseTime": "1960-01-01T00:00:00-0700", + "type": "release", + "minecraftArguments": "--version FML_DEV --tweakClass cpw.mods.fml.common.launcher.FMLTweaker", + "minimumLauncherVersion": 13, + "assets": "1.7.10", + "libraries": [ + { + "name": "net.minecraft:launchwrapper:1.11" + }, + { + "name": "org.ow2.asm:asm-debug-all:5.0.3", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-library:2.10.2", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "org.scala-lang:scala-compiler:2.10.2", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "net.sf.jopt-simple:jopt-simple:4.5", + "children" : ["sources"], + "url" : "http://repo.maven.apache.org/maven2" + }, + { + "name": "lzma:lzma:0.0.1" + }, + { + "name": "com.mojang:realms:1.3.5" + }, + { + "name": "org.apache.commons:commons-compress:1.8.1" + }, + { + "name": "org.apache.httpcomponents:httpclient:4.3.3" + }, + { + "name": "commons-logging:commons-logging:1.1.3" + }, + { + "name": "org.apache.httpcomponents:httpcore:4.3.2" + }, + { + "name": "java3d:vecmath:1.3.1" + }, + { + "name": "net.sf.trove4j:trove4j:3.0.3" + }, + { + "name": "com.ibm.icu:icu4j-core-mojang:51.2" + }, + { + "name": "net.sf.jopt-simple:jopt-simple:4.5" + }, + { + "name": "com.paulscode:codecjorbis:20101023" + }, + { + "name": "com.paulscode:codecwav:20101023" + }, + { + "name": "com.paulscode:libraryjavasound:20101123" + }, + { + "name": "com.paulscode:librarylwjglopenal:20100824" + }, + { + "name": "com.paulscode:soundsystem:20120107" + }, + { + "name": "io.netty:netty-all:4.0.10.Final" + }, + { + "name": "com.google.guava:guava:16.0" + }, + { + "name": "org.apache.commons:commons-lang3:3.2.1" + }, + { + "name": "commons-io:commons-io:2.4" + }, + { + "name": "commons-codec:commons-codec:1.9" + }, + { + "name": "net.java.jinput:jinput:2.0.5" + }, + { + "name": "net.java.jutils:jutils:1.0.0" + }, + { + "name": "com.google.code.gson:gson:2.2.4" + }, + { + "name": "com.mojang:authlib:1.5.16" + }, + { + "name": "org.apache.logging.log4j:log4j-api:2.0-beta9" + }, + { + "name": "org.apache.logging.log4j:log4j-core:2.0-beta9" + }, + { + "name": "org.lwjgl.lwjgl:lwjgl:2.9.1" + }, + { + "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.1" + }, + { + "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.1", + "natives": { + "linux": "natives-linux", + "windows": "natives-windows", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "net.java.jinput:jinput-platform:2.0.5", + "natives": { + "linux": "natives-linux", + "windows": "natives-windows", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "tv.twitch:twitch:5.16" + }, + { + "name": "tv.twitch:twitch-platform:5.16", + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "linux" + } + } + ], + "natives": { + "linux": "natives-linux", + "windows": "natives-windows-${arch}", + "osx": "natives-osx" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + }, + { + "name": "tv.twitch:twitch-external-platform:4.5", + "rules": [ + { + "action": "allow", + "os": { + "name": "windows" + } + } + ], + "natives": { + "windows": "natives-windows-${arch}" + }, + "extract": { + "exclude": [ + "META-INF/" + ] + } + } + ], + "mainClass": "net.minecraft.launchwrapper.Launch" +} diff --git a/patches/cpw/mods/fml/common/FMLCommonHandler.java.patch b/patches/cpw/mods/fml/common/FMLCommonHandler.java.patch new file mode 100644 index 0000000..78d0b52 --- /dev/null +++ b/patches/cpw/mods/fml/common/FMLCommonHandler.java.patch @@ -0,0 +1,58 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/FMLCommonHandler.java ++++ ../src-work/minecraft/cpw/mods/fml/common/FMLCommonHandler.java +@@ -37,6 +37,10 @@ + + import org.apache.logging.log4j.Level; + import org.apache.logging.log4j.Logger; ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.entity.Player; ++import org.bukkit.event.player.PlayerChangedWorldEvent; + + import com.google.common.base.Joiner; + import com.google.common.base.Strings; +@@ -385,10 +389,11 @@ + { + return; + } +- if (handlerSet.contains(handler)) ++ if (handlerSet.contains(handler) || worldInfo.getDimension() != 0) // Cauldron - Only check FML data in main world + { + return; + } ++ // Cauldron - logic below should only be run for overworld as Forge/Vanilla only use 1 SaveHandler + handlerSet.add(handler); + handlerToCheck = new WeakReference(handler); // for confirmBackupLevelDatUse + Map additionalProperties = Maps.newHashMap(); +@@ -496,7 +501,12 @@ + + public String getModName() + { +- List modNames = Lists.newArrayListWithExpectedSize(3); ++ // Cauldron start ++ List modNames = Lists.newArrayListWithExpectedSize(6); ++ modNames.add("cauldron"); ++ modNames.add("craftbukkit"); ++ modNames.add("mcpc"); ++ // Cauldron end + modNames.add("fml"); + if (!noForge) + { +@@ -540,8 +550,17 @@ + bus().post(new InputEvent.KeyInputEvent()); + } + ++ // Cauldron start - wrapper to notify plugins for mods that bypass ServerConfigurationManager + public void firePlayerChangedDimensionEvent(EntityPlayer player, int fromDim, int toDim) + { ++ this.firePlayerChangedDimensionEvent(player, fromDim, toDim, player.worldObj.getWorld()); ++ } ++ ++ public void firePlayerChangedDimensionEvent(EntityPlayer player, int fromDim, int toDim, CraftWorld fromWorld) ++ { ++ PlayerChangedWorldEvent event = new PlayerChangedWorldEvent((Player) player.getBukkitEntity(), fromWorld); ++ Bukkit.getServer().getPluginManager().callEvent(event); ++ // Cauldron end + bus().post(new PlayerEvent.PlayerChangedDimensionEvent(player, fromDim, toDim)); + } + diff --git a/patches/cpw/mods/fml/common/asm/FMLSanityChecker.java.patch b/patches/cpw/mods/fml/common/asm/FMLSanityChecker.java.patch new file mode 100644 index 0000000..a7588fd --- /dev/null +++ b/patches/cpw/mods/fml/common/asm/FMLSanityChecker.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/asm/FMLSanityChecker.java ++++ ../src-work/minecraft/cpw/mods/fml/common/asm/FMLSanityChecker.java +@@ -164,10 +164,12 @@ + FMLRelaunchLog.severe("Technical information: ClientBrandRetriever was at %s, there were %d certificates for it", codeSource.getLocation(), certCount); + } + } +- if (!goodFML) ++ // Cauldron start - disable message ++ /*if (!goodFML) + { + FMLRelaunchLog.severe("FML appears to be missing any signature data. This is not a good thing"); +- } ++ }*/ ++ // Cauldron end + return null; + } + diff --git a/patches/cpw/mods/fml/common/asm/transformers/SideTransformer.java.patch b/patches/cpw/mods/fml/common/asm/transformers/SideTransformer.java.patch new file mode 100644 index 0000000..2334528 --- /dev/null +++ b/patches/cpw/mods/fml/common/asm/transformers/SideTransformer.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/asm/transformers/SideTransformer.java ++++ ../src-work/minecraft/cpw/mods/fml/common/asm/transformers/SideTransformer.java +@@ -32,6 +32,7 @@ + { + private static String SIDE = FMLLaunchHandler.side().name(); + private static final boolean DEBUG = false; ++ public static boolean allowInvalidSide = false; // Cauldron + @Override + public byte[] transform(String name, String transformedName, byte[] bytes) + { +@@ -41,7 +42,7 @@ + ClassReader classReader = new ClassReader(bytes); + classReader.accept(classNode, 0); + +- if (remove((List)classNode.visibleAnnotations, SIDE)) ++ if (remove((List)classNode.visibleAnnotations, SIDE) && !allowInvalidSide) // Cauldron + { + if (DEBUG) + { diff --git a/patches/cpw/mods/fml/common/event/FMLServerStartingEvent.java.patch b/patches/cpw/mods/fml/common/event/FMLServerStartingEvent.java.patch new file mode 100644 index 0000000..ef64a4c --- /dev/null +++ b/patches/cpw/mods/fml/common/event/FMLServerStartingEvent.java.patch @@ -0,0 +1,23 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/event/FMLServerStartingEvent.java ++++ ../src-work/minecraft/cpw/mods/fml/common/event/FMLServerStartingEvent.java +@@ -16,6 +16,7 @@ + import net.minecraft.command.ICommand; + import net.minecraft.server.MinecraftServer; + import cpw.mods.fml.common.LoaderState.ModState; ++import org.bukkit.command.Command; // Cauldron + + public class FMLServerStartingEvent extends FMLStateEvent + { +@@ -43,4 +44,12 @@ + CommandHandler ch = (CommandHandler) getServer().getCommandManager(); + ch.registerCommand(command); + } ++ ++ // Cauldron start - used for mods to register a Bukkit command ++ public void registerServerCommand(String fallbackPrefix, Command command) ++ { ++ org.bukkit.command.SimpleCommandMap commandMap = getServer().server.getCommandMap(); ++ commandMap.register(fallbackPrefix, command); ++ } ++ // Cauldron end + } diff --git a/patches/cpw/mods/fml/common/network/handshake/ChannelRegistrationHandler.java.patch b/patches/cpw/mods/fml/common/network/handshake/ChannelRegistrationHandler.java.patch new file mode 100644 index 0000000..dce3768 --- /dev/null +++ b/patches/cpw/mods/fml/common/network/handshake/ChannelRegistrationHandler.java.patch @@ -0,0 +1,35 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/network/handshake/ChannelRegistrationHandler.java ++++ ../src-work/minecraft/cpw/mods/fml/common/network/handshake/ChannelRegistrationHandler.java +@@ -2,6 +2,8 @@ + + import io.netty.channel.ChannelHandlerContext; + import io.netty.channel.SimpleChannelInboundHandler; ++ ++import java.io.UnsupportedEncodingException; + import java.util.Set; + import net.minecraft.network.NetworkManager; + import org.apache.logging.log4j.Level; +@@ -24,6 +26,23 @@ + msg.payload().readBytes(data); + String channels = new String(data,Charsets.UTF_8); + String[] split = channels.split("\0"); ++ // Cauldron start - register bukkit channels for players ++ NetworkDispatcher dispatcher = ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).get(); ++ if (msg.channel().equals("REGISTER")) ++ { ++ for (String channel : split) ++ { ++ dispatcher.player.getBukkitEntity().addChannel(channel); ++ } ++ } ++ else ++ { ++ for (String channel : split) ++ { ++ dispatcher.player.getBukkitEntity().removeChannel(channel); ++ } ++ } ++ // Cauldron end + Set channelSet = ImmutableSet.copyOf(split); + FMLCommonHandler.instance().fireNetRegistrationEvent(manager, channelSet, msg.channel(), side); + } diff --git a/patches/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java.patch b/patches/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java.patch new file mode 100644 index 0000000..18ee8eb --- /dev/null +++ b/patches/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java.patch @@ -0,0 +1,45 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java ++++ ../src-work/minecraft/cpw/mods/fml/common/network/handshake/NetworkDispatcher.java +@@ -72,7 +72,7 @@ + public static final AttributeKey IS_LOCAL = new AttributeKey("fml:isLocal"); + public final NetworkManager manager; + private final ServerConfigurationManager scm; +- private EntityPlayerMP player; ++ public EntityPlayerMP player; // Cauldron + private ConnectionState state; + private ConnectionType connectionType; + private final Side side; +@@ -202,7 +202,7 @@ + } + else + { +- FMLLog.info("Unexpected packet during modded negotiation - assuming vanilla or keepalives : %s", msg.getClass().getName()); ++ //FMLLog.info("Unexpected packet during modded negotiation - assuming vanilla or keepalives : %s", msg.getClass().getName()); // Cauldron - unneeded spam + } + return false; + } +@@ -287,6 +287,7 @@ + state = ConnectionState.HANDSHAKING; + } + String channelName = msg.func_149559_c(); ++ player.getBukkitEntity().addChannel(channelName); // Cauldron - register channel for bukkit player + if ("FML|HS".equals(channelName) || "REGISTER".equals(channelName) || "UNREGISTER".equals(channelName)) + { + FMLProxyPacket proxy = new FMLProxyPacket(msg); +@@ -308,6 +309,7 @@ + else if (NetworkRegistry.INSTANCE.hasChannel(channelName, Side.SERVER)) + { + FMLProxyPacket proxy = new FMLProxyPacket(msg); ++ serverHandler.getCraftServer().getMessenger().dispatchIncomingMessage(player.getBukkitEntity(), msg.func_149559_c(), msg.func_149558_e()); // pass msg to bukkit + proxy.setDispatcher(this); + context.fireChannelRead(proxy); + return true; +@@ -465,7 +467,7 @@ + // Stop the epic channel closed spam at close + if (!(cause instanceof ClosedChannelException)) + { +- FMLLog.log(Level.ERROR, cause, "NetworkDispatcher exception"); ++ //FMLLog.log(Level.ERROR, cause, "NetworkDispatcher exception"); // Cauldron - disable unneeded spam + } + super.exceptionCaught(ctx, cause); + } diff --git a/patches/cpw/mods/fml/common/network/internal/FMLNetworkHandler.java.patch b/patches/cpw/mods/fml/common/network/internal/FMLNetworkHandler.java.patch new file mode 100644 index 0000000..f708828 --- /dev/null +++ b/patches/cpw/mods/fml/common/network/internal/FMLNetworkHandler.java.patch @@ -0,0 +1,51 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/network/internal/FMLNetworkHandler.java ++++ ../src-work/minecraft/cpw/mods/fml/common/network/internal/FMLNetworkHandler.java +@@ -47,6 +47,16 @@ + import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration; + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; ++//Cauldron start ++import net.minecraft.inventory.IInventory; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.tileentity.TileEntity; ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++import org.bukkit.event.inventory.InventoryType; ++// Cauldron end + + public class FMLNetworkHandler + { +@@ -75,6 +85,31 @@ + Container remoteGuiContainer = NetworkRegistry.INSTANCE.getRemoteGuiContainer(mc, entityPlayerMP, modGuiId, world, x, y, z); + if (remoteGuiContainer != null) + { ++ // Cauldron start - create bukkitView for passed container then fire open event. ++ if (entityPlayer != null) ++ { ++ if (remoteGuiContainer.getBukkitView() == null) ++ { ++ TileEntity te = entityPlayer.worldObj.getTileEntity(x, y, z); ++ if (te != null && te instanceof IInventory) ++ { ++ IInventory teInv = (IInventory)te; ++ CraftInventory inventory = new CraftInventory(teInv); ++ remoteGuiContainer.bukkitView = new CraftInventoryView(entityPlayer.getBukkitEntity(), inventory, remoteGuiContainer); ++ } ++ else ++ { ++ remoteGuiContainer.bukkitView = new CraftInventoryView(entityPlayer.getBukkitEntity(), MinecraftServer.getServer().server.createInventory(entityPlayer.getBukkitEntity(), InventoryType.CHEST), remoteGuiContainer); ++ } ++ ++ remoteGuiContainer = CraftEventFactory.callInventoryOpenEvent((EntityPlayerMP)entityPlayer, remoteGuiContainer, false); ++ if (remoteGuiContainer == null) ++ { ++ return; ++ } ++ } ++ } ++ // Cauldron end + entityPlayerMP.getNextWindowId(); + entityPlayerMP.closeContainer(); + int windowId = entityPlayerMP.currentWindowId; diff --git a/patches/cpw/mods/fml/common/network/internal/HandshakeCompletionHandler.java.patch b/patches/cpw/mods/fml/common/network/internal/HandshakeCompletionHandler.java.patch new file mode 100644 index 0000000..64edf7c --- /dev/null +++ b/patches/cpw/mods/fml/common/network/internal/HandshakeCompletionHandler.java.patch @@ -0,0 +1,18 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/network/internal/HandshakeCompletionHandler.java ++++ ../src-work/minecraft/cpw/mods/fml/common/network/internal/HandshakeCompletionHandler.java +@@ -13,9 +13,15 @@ + @Override + protected void channelRead0(ChannelHandlerContext ctx, CompleteHandshake msg) throws Exception + { ++ // Cauldron start - attempt to fix race condition with attr being null ++ Object attr = ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER); ++ if (attr != null) ++ { + NetworkDispatcher dispatcher = ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).getAndRemove(); + dispatcher.completeHandshake(msg.target); + } ++ // Cauldron end ++ } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception diff --git a/patches/cpw/mods/fml/common/registry/EntityRegistry.java.patch b/patches/cpw/mods/fml/common/registry/EntityRegistry.java.patch new file mode 100644 index 0000000..5cf25ea --- /dev/null +++ b/patches/cpw/mods/fml/common/registry/EntityRegistry.java.patch @@ -0,0 +1,79 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/registry/EntityRegistry.java ++++ ../src-work/minecraft/cpw/mods/fml/common/registry/EntityRegistry.java +@@ -41,6 +41,12 @@ + import cpw.mods.fml.common.ModContainer; + import cpw.mods.fml.common.network.internal.FMLMessage.EntitySpawnMessage; + ++// Cauldron start ++import net.minecraftforge.common.util.EnumHelper; ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.entity.EntityType; ++// Cauldron end ++ + public class EntityRegistry + { + public class EntityRegistration +@@ -118,6 +124,8 @@ + private ListMultimap entityRegistrations = ArrayListMultimap.create(); + private Map entityNames = Maps.newHashMap(); + private BiMap, EntityRegistration> entityClassRegistrations = HashBiMap.create(); ++ public static Map, String> entityTypeMap = Maps.newHashMap(); // Cauldron - used by CraftCustomEntity ++ public static Map> entityClassMap = Maps.newHashMap(); // Cauldron - used by CraftWorld + public static EntityRegistry instance() + { + return INSTANCE; +@@ -147,6 +155,7 @@ + public static void registerModEntity(Class entityClass, String entityName, int id, Object mod, int trackingRange, int updateFrequency, boolean sendsVelocityUpdates) + { + instance().doModEntityRegistration(entityClass, entityName, id, mod, trackingRange, updateFrequency, sendsVelocityUpdates); ++ registerBukkitType(entityClass, entityName); // Cauldron - register EntityType for Bukkit + } + + @SuppressWarnings("unchecked") +@@ -197,6 +206,7 @@ + } + id = instance().validateAndClaimId(id); + EntityList.addMapping(entityClass, entityName, id); ++ registerBukkitType(entityClass, entityName); // Cauldron - register EntityType for Bukkit + } + + private int validateAndClaimId(int id) +@@ -249,8 +259,38 @@ + } + instance().validateAndClaimId(id); + EntityList.addMapping(entityClass, entityName, id, backgroundEggColour, foregroundEggColour); ++ registerBukkitType(entityClass, entityName); // Cauldron - register EntityType for Bukkit + } + ++ // Cauldron start ++ private static void registerBukkitType(Class entityClass, String entityName) ++ { ++ ModContainer activeModContainer = Loader.instance().activeModContainer(); ++ String modId = "unknown"; ++ // fixup bad entity names from mods ++ if (entityName.contains(".")) ++ { ++ if ((entityName.indexOf(".") + 1) < entityName.length()) ++ entityName = entityName.substring(entityName.indexOf(".") + 1, entityName.length()); ++ } ++ entityName.replace("entity", ""); ++ if (entityName.startsWith("ent")) ++ entityName.replace("ent", ""); ++ entityName = entityName.replaceAll("[^A-Za-z0-9]", ""); // remove all non-digits/alphanumeric ++ if (activeModContainer != null) ++ modId = activeModContainer.getModId(); ++ entityName = modId + "-" + entityName; ++ entityTypeMap.put(entityClass, entityName); ++ entityClassMap.put(entityName, entityClass); ++ } ++ ++ // used by CraftCustomEntity ++ public static String getCustomEntityTypeName(Class entityClass) ++ { ++ return entityTypeMap.get(entityClass); ++ } ++ // Cauldron end ++ + public static void addSpawn(Class entityClass, int weightedProb, int min, int max, EnumCreatureType typeOfCreature, BiomeGenBase... biomes) + { + for (BiomeGenBase biome : biomes) diff --git a/patches/cpw/mods/fml/common/registry/GameData.java.patch b/patches/cpw/mods/fml/common/registry/GameData.java.patch new file mode 100644 index 0000000..0536ee0 --- /dev/null +++ b/patches/cpw/mods/fml/common/registry/GameData.java.patch @@ -0,0 +1,69 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/registry/GameData.java ++++ ../src-work/minecraft/cpw/mods/fml/common/registry/GameData.java +@@ -14,7 +14,9 @@ + + import java.io.File; + import java.io.IOException; ++import java.util.ArrayList; + import java.util.BitSet; ++import java.util.Collections; + import java.util.HashMap; + import java.util.HashSet; + import java.util.Iterator; +@@ -1024,4 +1026,56 @@ + throw new RuntimeException("WHAT?"); + } + } ++ ++ // Cauldron start ++ public static void injectItemBukkitMaterials() ++ { ++ FMLControlledNamespacedRegistry itemRegistry = getItemRegistry(); ++ List ids = new ArrayList(); ++ ++ for (Item thing : itemRegistry.typeSafeIterable()) ++ { ++ ids.add(itemRegistry.getId(thing)); ++ } ++ ++ // sort by id ++ Collections.sort(ids); ++ ++ for (int id : ids) ++ { ++ Item item = itemRegistry.getRaw(id); ++ // inject item materials into Bukkit for FML ++ org.bukkit.Material material = org.bukkit.Material.addMaterial(id, itemRegistry.getNameForObject(item), false); ++ if (material != null) ++ { ++ FMLLog.info("Injected new Forge item material %s with ID %d.", material.name(), material.getId()); ++ } ++ } ++ } ++ ++ public static void injectBlockBukkitMaterials() ++ { ++ FMLControlledNamespacedRegistry blockRegistry = getBlockRegistry(); ++ List ids = new ArrayList(); ++ ++ for (Block block : blockRegistry.typeSafeIterable()) ++ { ++ ids.add(blockRegistry.getId(block)); ++ } ++ ++ // sort by id ++ Collections.sort(ids); ++ ++ for (int id : ids) ++ { ++ Block block = blockRegistry.getRaw(id); ++ // inject block materials into Bukkit for FML ++ org.bukkit.Material material = org.bukkit.Material.addMaterial(id, blockRegistry.getNameForObject(block), true); ++ if (material != null) ++ { ++ FMLLog.info("Injected new Forge block material %s with ID %d.", material.name(), material.getId()); ++ } ++ } ++ } ++ // Cauldron end + } diff --git a/patches/cpw/mods/fml/common/registry/GameRegistry.java.patch b/patches/cpw/mods/fml/common/registry/GameRegistry.java.patch new file mode 100644 index 0000000..86c77cd --- /dev/null +++ b/patches/cpw/mods/fml/common/registry/GameRegistry.java.patch @@ -0,0 +1,71 @@ +--- ../src-base/minecraft/cpw/mods/fml/common/registry/GameRegistry.java ++++ ../src-work/minecraft/cpw/mods/fml/common/registry/GameRegistry.java +@@ -53,6 +53,7 @@ + import cpw.mods.fml.common.LoaderException; + import cpw.mods.fml.common.LoaderState; + import cpw.mods.fml.common.ObfuscationReflectionHelper; ++import java.util.HashMap; // Cauldron + + public class GameRegistry + { +@@ -60,6 +61,10 @@ + private static Map worldGeneratorIndex = Maps.newHashMap(); + private static List fuelHandlers = Lists.newArrayList(); + private static List sortedGeneratorList; ++ // Cauldron start ++ private static Map configWorldGenCache = new HashMap(); ++ private static Map worldGenMap = new HashMap(); ++ // Cauldron end + + /** + * Register a world generator - something that inserts new block types into the world +@@ -70,12 +75,18 @@ + */ + public static void registerWorldGenerator(IWorldGenerator generator, int modGenerationWeight) + { ++ // Cauldron start - mod id's are not available during generateWorld so we must capture them here ++ String modId = Loader.instance().activeModContainer().getModId(); ++ modId = modId.replaceAll("[^A-Za-z0-9]", ""); // remove all non-digits/alphanumeric ++ modId.replace(" ", "_"); + worldGenerators.add(generator); + worldGeneratorIndex.put(generator, modGenerationWeight); + if (sortedGeneratorList != null) + { + sortedGeneratorList = null; + } ++ worldGenMap.put(generator.getClass().getName(), modId); ++ // Cauldron end + } + + /** +@@ -100,11 +111,27 @@ + long zSeed = fmlRandom.nextLong() >> 2 + 1L; + long chunkSeed = (xSeed * chunkX + zSeed * chunkZ) ^ worldSeed; + +- for (IWorldGenerator generator : sortedGeneratorList) ++ boolean before = ((net.minecraft.world.WorldServer) world).theChunkProviderServer.loadChunkOnProvideRequest; // Cauldron store value ++ ((net.minecraft.world.WorldServer) world).theChunkProviderServer.loadChunkOnProvideRequest = true; // Cauldron load chunks on provide requests ++ for (IWorldGenerator generator : worldGenerators) + { +- fmlRandom.setSeed(chunkSeed); +- generator.generate(fmlRandom, chunkX, chunkZ, world, chunkGenerator, chunkProvider); ++ // Cauldron start ++ if (!configWorldGenCache.containsKey(generator.getClass().getName())) ++ { ++ String modId = worldGenMap.get(generator.getClass().getName()); ++ String generatorName = ""; ++ generatorName = modId + "-" + generator.getClass().getSimpleName(); ++ boolean generatorEnabled = world.cauldronConfig.getBoolean("worldgen-" + generatorName, true); ++ configWorldGenCache.put(generator.getClass().getName(), generatorEnabled); ++ } ++ if (configWorldGenCache.get(generator.getClass().getName())) ++ { ++ fmlRandom.setSeed(chunkSeed); ++ generator.generate(fmlRandom, chunkX, chunkZ, world, chunkGenerator, chunkProvider); ++ } + } ++ ((net.minecraft.world.WorldServer)world).theChunkProviderServer.loadChunkOnProvideRequest = before; // reset ++ // Cauldron end + } + + private static void computeSortedGeneratorList() diff --git a/patches/cpw/mods/fml/relauncher/CoreModManager.java.patch b/patches/cpw/mods/fml/relauncher/CoreModManager.java.patch new file mode 100644 index 0000000..4111d3a --- /dev/null +++ b/patches/cpw/mods/fml/relauncher/CoreModManager.java.patch @@ -0,0 +1,47 @@ +--- ../src-base/minecraft/cpw/mods/fml/relauncher/CoreModManager.java ++++ ../src-work/minecraft/cpw/mods/fml/relauncher/CoreModManager.java +@@ -153,6 +153,9 @@ + + } + ++ // Cauldron - group output of @MCVersion warnings ++ private static List noVersionAnnotationCoreMods = new ArrayList(); ++ + public static void handleLaunch(File mcDir, LaunchClassLoader classLoader, FMLTweaker tweaker) + { + CoreModManager.mcDir = mcDir; +@@ -212,7 +215,19 @@ + loadCoreMod(classLoader, coreModClassName, null); + } + discoverCoreMods(mcDir, classLoader); +- ++ // Cauldron start - group output of @MCVersion warnings ++ if (!noVersionAnnotationCoreMods.isEmpty()) ++ { ++ FMLRelaunchLog ++ .warning("The following coremods do not have a @MCVersion annotation. They may cause problems if this is not the correct version of Minecraft for them."); ++ StringBuilder sb = new StringBuilder("Missing @MCVersion: "); ++ for (String className : noVersionAnnotationCoreMods) ++ { ++ sb.append(className).append(" "); ++ } ++ FMLRelaunchLog.warning(sb.toString()); ++ } ++ // Cauldron end + } + + private static void discoverCoreMods(File mcDir, LaunchClassLoader classLoader) +@@ -424,8 +439,11 @@ + MCVersion requiredMCVersion = coreModClazz.getAnnotation(IFMLLoadingPlugin.MCVersion.class); + if (!Arrays.asList(rootPlugins).contains(coreModClass) && (requiredMCVersion == null || Strings.isNullOrEmpty(requiredMCVersion.value()))) + { +- FMLRelaunchLog.log(Level.WARN, "The coremod %s does not have a MCVersion annotation, it may cause issues with this version of Minecraft", +- coreModClass); ++ // Cauldron start - group output of @MCVersion warnings ++ // FMLRelaunchLog.log(Level.WARN, "The coremod %s does not have a MCVersion annotation, it may cause issues with this version of Minecraft", ++ // coreModClass); ++ noVersionAnnotationCoreMods.add(coreModClass); ++ // Cauldron end + } + else if (requiredMCVersion != null && !FMLInjectionData.mccversion.equals(requiredMCVersion.value())) + { diff --git a/patches/net/minecraft/block/Block.java.patch b/patches/net/minecraft/block/Block.java.patch new file mode 100644 index 0000000..75c1970 --- /dev/null +++ b/patches/net/minecraft/block/Block.java.patch @@ -0,0 +1,52 @@ +--- ../src-base/minecraft/net/minecraft/block/Block.java ++++ ../src-work/minecraft/net/minecraft/block/Block.java +@@ -122,6 +122,7 @@ + private String unlocalizedName; + @SideOnly(Side.CLIENT) + protected IIcon blockIcon; ++ public boolean isForgeBlock; // Cauldron + private static final String __OBFID = "CL_00000199"; + + public final cpw.mods.fml.common.registry.RegistryDelegate delegate = +@@ -650,7 +651,7 @@ + + public void dropBlockAsItemWithChance(World p_149690_1_, int p_149690_2_, int p_149690_3_, int p_149690_4_, int p_149690_5_, float p_149690_6_, int p_149690_7_) + { +- if (!p_149690_1_.isRemote && !p_149690_1_.restoringBlockSnapshots) // do not drop items while restoring blockstates, prevents item dupe ++ if (!p_149690_1_.isRemote && (!p_149690_1_.restoringBlockSnapshots || !p_149690_1_.restoringBlockStates)) // do not drop items while restoring blockstates, prevents item dupe + { + ArrayList items = getDrops(p_149690_1_, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, p_149690_7_); + p_149690_6_ = ForgeEventFactory.fireBlockHarvesting(items, p_149690_1_, this, p_149690_2_, p_149690_3_, p_149690_4_, p_149690_5_, p_149690_7_, p_149690_6_, false, harvesters.get()); +@@ -667,7 +668,7 @@ + + protected void dropBlockAsItem(World p_149642_1_, int p_149642_2_, int p_149642_3_, int p_149642_4_, ItemStack p_149642_5_) + { +- if (!p_149642_1_.isRemote && p_149642_1_.getGameRules().getGameRuleBooleanValue("doTileDrops") && !p_149642_1_.restoringBlockSnapshots) // do not drop items while restoring blockstates, prevents item dupe ++ if (!p_149642_1_.isRemote && p_149642_1_.getGameRules().getGameRuleBooleanValue("doTileDrops") && (!p_149642_1_.restoringBlockSnapshots || !p_149642_1_.restoringBlockStates)) // do not drop items while restoring blockstates, prevents item dupe + { + if (captureDrops.get()) + { +@@ -1131,6 +1132,23 @@ + return this; + } + ++ // Spigot start ++ public static float range(float min, float value, float max) ++ { ++ if (value < min) ++ { ++ return min; ++ } ++ ++ if (value > max) ++ { ++ return max; ++ } ++ ++ return value; ++ } ++ // Spigot end ++ + @SideOnly(Side.CLIENT) + protected String getTextureName() + { diff --git a/patches/net/minecraft/block/BlockBasePressurePlate.java.patch b/patches/net/minecraft/block/BlockBasePressurePlate.java.patch new file mode 100644 index 0000000..ddfb36a --- /dev/null +++ b/patches/net/minecraft/block/BlockBasePressurePlate.java.patch @@ -0,0 +1,32 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockBasePressurePlate.java ++++ ../src-work/minecraft/net/minecraft/block/BlockBasePressurePlate.java +@@ -11,6 +11,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public abstract class BlockBasePressurePlate extends Block + { + private String field_150067_a; +@@ -122,7 +124,20 @@ + int i1 = this.func_150065_e(p_150062_1_, p_150062_2_, p_150062_3_, p_150062_4_); + boolean flag = p_150062_5_ > 0; + boolean flag1 = i1 > 0; ++ // CraftBukkit start - Interact Pressure Plate ++ org.bukkit.World bworld = p_150062_1_.getWorld(); ++ org.bukkit.plugin.PluginManager manager = p_150062_1_.getServer().getPluginManager(); + ++ if (flag != flag1) ++ { ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bworld.getBlockAt(p_150062_2_, p_150062_3_, p_150062_4_), p_150062_5_, i1); ++ manager.callEvent(eventRedstone); ++ flag1 = eventRedstone.getNewCurrent() > 0; ++ i1 = eventRedstone.getNewCurrent(); ++ } ++ ++ // CraftBukkit end ++ + if (p_150062_5_ != i1) + { + p_150062_1_.setBlockMetadataWithNotify(p_150062_2_, p_150062_3_, p_150062_4_, this.func_150066_d(i1), 2); diff --git a/patches/net/minecraft/block/BlockButton.java.patch b/patches/net/minecraft/block/BlockButton.java.patch new file mode 100644 index 0000000..bf1e5db --- /dev/null +++ b/patches/net/minecraft/block/BlockButton.java.patch @@ -0,0 +1,89 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockButton.java ++++ ../src-work/minecraft/net/minecraft/block/BlockButton.java +@@ -16,6 +16,10 @@ + + import net.minecraftforge.common.util.ForgeDirection; + import static net.minecraftforge.common.util.ForgeDirection.*; ++// CraftBukkit start ++import org.bukkit.event.block.BlockRedstoneEvent; ++import org.bukkit.event.entity.EntityInteractEvent; ++// CraftBukkit end + + public abstract class BlockButton extends Block + { +@@ -209,6 +213,19 @@ + } + else + { ++ // CraftBukkit start ++ org.bukkit.block.Block block = p_149727_1_.getWorld().getBlockAt(p_149727_2_, p_149727_3_, p_149727_4_); ++ int old = (k1 != 8) ? 15 : 0; ++ int current = (k1 == 8) ? 15 : 0; ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); ++ p_149727_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if ((eventRedstone.getNewCurrent() > 0) != (k1 == 8)) ++ { ++ return true; ++ } ++ ++ // CraftBukkit end + p_149727_1_.setBlockMetadataWithNotify(p_149727_2_, p_149727_3_, p_149727_4_, j1 + k1, 3); + p_149727_1_.markBlockRangeForRenderUpdate(p_149727_2_, p_149727_3_, p_149727_4_, p_149727_2_, p_149727_3_, p_149727_4_); + p_149727_1_.playSoundEffect((double)p_149727_2_ + 0.5D, (double)p_149727_3_ + 0.5D, (double)p_149727_4_ + 0.5D, "random.click", 0.3F, 0.6F); +@@ -262,6 +279,18 @@ + + if ((l & 8) != 0) + { ++ // CraftBukkit start ++ org.bukkit.block.Block block = p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_); ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); ++ p_149674_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if (eventRedstone.getNewCurrent() > 0) ++ { ++ return; ++ } ++ ++ // CraftBukkit end ++ + if (this.field_150047_a) + { + this.func_150046_n(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_); +@@ -309,6 +338,36 @@ + List list = p_150046_1_.getEntitiesWithinAABB(EntityArrow.class, AxisAlignedBB.getBoundingBox((double)p_150046_2_ + this.minX, (double)p_150046_3_ + this.minY, (double)p_150046_4_ + this.minZ, (double)p_150046_2_ + this.maxX, (double)p_150046_3_ + this.maxY, (double)p_150046_4_ + this.maxZ)); + boolean flag1 = !list.isEmpty(); + ++ // CraftBukkit start - Call interact event when arrows turn on wooden buttons ++ if (flag != flag1 && flag1) ++ { ++ org.bukkit.block.Block block = p_150046_1_.getWorld().getBlockAt(p_150046_2_, p_150046_3_, p_150046_4_); ++ boolean allowed = false; ++ ++ // If all of the events are cancelled block the button press, else allow ++ for (Object object : list) ++ { ++ if (object != null) ++ { ++ EntityInteractEvent event = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block); ++ p_150046_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ allowed = true; ++ break; ++ } ++ } ++ } ++ ++ if (!allowed) ++ { ++ return; ++ } ++ } ++ ++ // CraftBukkit end ++ + if (flag1 && !flag) + { + p_150046_1_.setBlockMetadataWithNotify(p_150046_2_, p_150046_3_, p_150046_4_, i1 | 8, 3); diff --git a/patches/net/minecraft/block/BlockCactus.java.patch b/patches/net/minecraft/block/BlockCactus.java.patch new file mode 100644 index 0000000..101c112 --- /dev/null +++ b/patches/net/minecraft/block/BlockCactus.java.patch @@ -0,0 +1,31 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockCactus.java ++++ ../src-work/minecraft/net/minecraft/block/BlockCactus.java +@@ -17,6 +17,8 @@ + import net.minecraftforge.common.util.ForgeDirection; + import net.minecraftforge.common.IPlantable; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockCactus extends Block implements IPlantable + { + @SideOnly(Side.CLIENT) +@@ -47,9 +49,9 @@ + { + int i1 = p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_, p_149674_4_); + +- if (i1 == 15) ++ if (i1 >= (byte) range(3, (p_149674_1_.growthOdds / p_149674_1_.getSpigotConfig().cactusModifier * 15) + 0.5F, 15)) // Spigot // Cauldron + { +- p_149674_1_.setBlock(p_149674_2_, p_149674_3_ + 1, p_149674_4_, this); ++ CraftEventFactory.handleBlockGrowEvent(p_149674_1_, p_149674_2_, p_149674_3_ + 1, p_149674_4_, this, 0); // CraftBukkit + p_149674_1_.setBlockMetadataWithNotify(p_149674_2_, p_149674_3_, p_149674_4_, 0, 4); + this.onNeighborBlockChange(p_149674_1_, p_149674_2_, p_149674_3_ + 1, p_149674_4_, this); + } +@@ -135,6 +137,7 @@ + + public void onEntityCollidedWithBlock(World p_149670_1_, int p_149670_2_, int p_149670_3_, int p_149670_4_, Entity p_149670_5_) + { ++ // Cauldron - moved CraftBukkit hook to func_145775_I() - doBlockCollisions + p_149670_5_.attackEntityFrom(DamageSource.cactus, 1.0F); + } + diff --git a/patches/net/minecraft/block/BlockCake.java.patch b/patches/net/minecraft/block/BlockCake.java.patch new file mode 100644 index 0000000..2e51ff8 --- /dev/null +++ b/patches/net/minecraft/block/BlockCake.java.patch @@ -0,0 +1,32 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockCake.java ++++ ../src-work/minecraft/net/minecraft/block/BlockCake.java +@@ -12,6 +12,10 @@ + import net.minecraft.util.IIcon; + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; ++// CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.network.play.server.S06PacketUpdateHealth; ++// CraftBukkit end + + public class BlockCake extends Block + { +@@ -104,7 +108,17 @@ + { + if (p_150036_5_.canEat(false)) + { +- p_150036_5_.getFoodStats().addStats(2, 0.1F); ++ // CraftBukkit start ++ int oldFoodLevel = p_150036_5_.getFoodStats().foodLevel; ++ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(p_150036_5_, 2 + oldFoodLevel); ++ ++ if (!event.isCancelled()) ++ { ++ p_150036_5_.getFoodStats().addStats(event.getFoodLevel() - oldFoodLevel, 0.1F); ++ } ++ ++ ((EntityPlayerMP) p_150036_5_).playerNetServerHandler.sendPacket(new S06PacketUpdateHealth(((EntityPlayerMP) p_150036_5_).getBukkitEntity().getScaledHealth(), p_150036_5_.getFoodStats().foodLevel, p_150036_5_.getFoodStats().foodSaturationLevel)); ++ // CraftBukkit end + int l = p_150036_1_.getBlockMetadata(p_150036_2_, p_150036_3_, p_150036_4_) + 1; + + if (l >= 6) diff --git a/patches/net/minecraft/block/BlockCocoa.java.patch b/patches/net/minecraft/block/BlockCocoa.java.patch new file mode 100644 index 0000000..0bf8491 --- /dev/null +++ b/patches/net/minecraft/block/BlockCocoa.java.patch @@ -0,0 +1,12 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockCocoa.java ++++ ../src-work/minecraft/net/minecraft/block/BlockCocoa.java +@@ -51,7 +51,8 @@ + if (i1 < 2) + { + ++i1; +- p_149674_1_.setBlockMetadataWithNotify(p_149674_2_, p_149674_3_, p_149674_4_, i1 << 2 | getDirection(l), 2); ++ // CraftBukkit ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, this, i1 << 2 | getDirection(l)); + } + } + } diff --git a/patches/net/minecraft/block/BlockCommandBlock.java.patch b/patches/net/minecraft/block/BlockCommandBlock.java.patch new file mode 100644 index 0000000..39726f2 --- /dev/null +++ b/patches/net/minecraft/block/BlockCommandBlock.java.patch @@ -0,0 +1,34 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockCommandBlock.java ++++ ../src-work/minecraft/net/minecraft/block/BlockCommandBlock.java +@@ -10,6 +10,8 @@ + import net.minecraft.tileentity.TileEntityCommandBlock; + import net.minecraft.world.World; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockCommandBlock extends BlockContainer + { + private static final String __OBFID = "CL_00000219"; +@@ -31,13 +33,20 @@ + boolean flag = p_149695_1_.isBlockIndirectlyGettingPowered(p_149695_2_, p_149695_3_, p_149695_4_); + int l = p_149695_1_.getBlockMetadata(p_149695_2_, p_149695_3_, p_149695_4_); + boolean flag1 = (l & 1) != 0; ++ // CraftBukkit start ++ org.bukkit.block.Block bukkitBlock = p_149695_1_.getWorld().getBlockAt(p_149695_2_, p_149695_3_, p_149695_4_); ++ int old = flag1 ? 15 : 0; ++ int current = flag ? 15 : 0; ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, old, current); ++ p_149695_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ // CraftBukkit end + +- if (flag && !flag1) ++ if (eventRedstone.getNewCurrent() > 0 && !(eventRedstone.getOldCurrent() > 0)) // CraftBukkit + { + p_149695_1_.setBlockMetadataWithNotify(p_149695_2_, p_149695_3_, p_149695_4_, l | 1, 4); + p_149695_1_.scheduleBlockUpdate(p_149695_2_, p_149695_3_, p_149695_4_, this, this.tickRate(p_149695_1_)); + } +- else if (!flag && flag1) ++ else if (!(eventRedstone.getNewCurrent() > 0) && eventRedstone.getOldCurrent() > 0) // CraftBukkit + { + p_149695_1_.setBlockMetadataWithNotify(p_149695_2_, p_149695_3_, p_149695_4_, l & -2, 4); + } diff --git a/patches/net/minecraft/block/BlockCrops.java.patch b/patches/net/minecraft/block/BlockCrops.java.patch new file mode 100644 index 0000000..4e2dd23 --- /dev/null +++ b/patches/net/minecraft/block/BlockCrops.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockCrops.java ++++ ../src-work/minecraft/net/minecraft/block/BlockCrops.java +@@ -49,10 +49,9 @@ + { + float f = this.func_149864_n(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_); + +- if (p_149674_5_.nextInt((int)(25.0F / f) + 1) == 0) ++ if (p_149674_5_.nextInt((int)(p_149674_1_.growthOdds / p_149674_1_.getSpigotConfig().wheatModifier * (25.0F / f)) + 1) == 0) // Spigot // Cauldron + { +- ++l; +- p_149674_1_.setBlockMetadataWithNotify(p_149674_2_, p_149674_3_, p_149674_4_, l, 2); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, this, ++l); // CraftBukkit + } + } + } diff --git a/patches/net/minecraft/block/BlockDaylightDetector.java.patch b/patches/net/minecraft/block/BlockDaylightDetector.java.patch new file mode 100644 index 0000000..4106fcc --- /dev/null +++ b/patches/net/minecraft/block/BlockDaylightDetector.java.patch @@ -0,0 +1,10 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockDaylightDetector.java ++++ ../src-work/minecraft/net/minecraft/block/BlockDaylightDetector.java +@@ -73,6 +73,7 @@ + + if (l != i1) + { ++ i1 = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(p_149957_1_, p_149957_2_, p_149957_3_, p_149957_4_, l, i1).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent + p_149957_1_.setBlockMetadataWithNotify(p_149957_2_, p_149957_3_, p_149957_4_, i1, 3); + } + } diff --git a/patches/net/minecraft/block/BlockDispenser.java.patch b/patches/net/minecraft/block/BlockDispenser.java.patch new file mode 100644 index 0000000..c52cd2c --- /dev/null +++ b/patches/net/minecraft/block/BlockDispenser.java.patch @@ -0,0 +1,28 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockDispenser.java ++++ ../src-work/minecraft/net/minecraft/block/BlockDispenser.java +@@ -30,6 +30,7 @@ + { + public static final IRegistry dispenseBehaviorRegistry = new RegistryDefaulted(new BehaviorDefaultDispenseItem()); + protected Random field_149942_b = new Random(); ++ public static boolean eventFired = false; // CraftBukkit + @SideOnly(Side.CLIENT) + protected IIcon field_149944_M; + @SideOnly(Side.CLIENT) +@@ -124,7 +125,8 @@ + } + } + +- protected void func_149941_e(World p_149941_1_, int p_149941_2_, int p_149941_3_, int p_149941_4_) ++ // CraftBukkit - protected -> public ++ public void func_149941_e(World p_149941_1_, int p_149941_2_, int p_149941_3_, int p_149941_4_) + { + BlockSourceImpl blocksourceimpl = new BlockSourceImpl(p_149941_1_, p_149941_2_, p_149941_3_, p_149941_4_); + TileEntityDispenser tileentitydispenser = (TileEntityDispenser)blocksourceimpl.getBlockTileEntity(); +@@ -145,6 +147,7 @@ + if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider) + { + ItemStack itemstack1 = ibehaviordispenseitem.dispense(blocksourceimpl, itemstack); ++ eventFired = false; // CraftBukkit - reset event status + tileentitydispenser.setInventorySlotContents(l, itemstack1.stackSize == 0 ? null : itemstack1); + } + } diff --git a/patches/net/minecraft/block/BlockDoor.java.patch b/patches/net/minecraft/block/BlockDoor.java.patch new file mode 100644 index 0000000..1f0425a --- /dev/null +++ b/patches/net/minecraft/block/BlockDoor.java.patch @@ -0,0 +1,48 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockDoor.java ++++ ../src-work/minecraft/net/minecraft/block/BlockDoor.java +@@ -16,6 +16,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockDoor extends Block + { + @SideOnly(Side.CLIENT) +@@ -329,15 +331,32 @@ + { + this.dropBlockAsItem(p_149695_1_, p_149695_2_, p_149695_3_, p_149695_4_, l, 0); + } ++ ++ // CraftBukkit start + } +- else ++ else if (p_149695_5_.canProvidePower()) + { +- boolean flag1 = p_149695_1_.isBlockIndirectlyGettingPowered(p_149695_2_, p_149695_3_, p_149695_4_) || p_149695_1_.isBlockIndirectlyGettingPowered(p_149695_2_, p_149695_3_ + 1, p_149695_4_); ++ org.bukkit.World bworld = p_149695_1_.getWorld(); ++ org.bukkit.block.Block bukkitBlock = bworld.getBlockAt(p_149695_2_, p_149695_3_, p_149695_4_); ++ org.bukkit.block.Block blockTop = bworld.getBlockAt(p_149695_2_, p_149695_3_ + 1, p_149695_4_); ++ int power = bukkitBlock.getBlockPower(); ++ int powerTop = blockTop.getBlockPower(); + +- if ((flag1 || p_149695_5_.canProvidePower()) && p_149695_5_ != this) ++ if (powerTop > power) + { +- this.func_150014_a(p_149695_1_, p_149695_2_, p_149695_3_, p_149695_4_, flag1); ++ power = powerTop; + } ++ ++ int oldPower = (p_149695_1_.getBlockMetadata(p_149695_2_, p_149695_3_, p_149695_4_) & 4) > 0 ? 15 : 0; ++ ++ if (oldPower == 0 ^ power == 0) ++ { ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, oldPower, power); ++ p_149695_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ this.func_150014_a(p_149695_1_, p_149695_2_, p_149695_3_, p_149695_4_, eventRedstone.getNewCurrent() > 0); ++ } ++ ++ // CraftBukkit end + } + } + else diff --git a/patches/net/minecraft/block/BlockDragonEgg.java.patch b/patches/net/minecraft/block/BlockDragonEgg.java.patch new file mode 100644 index 0000000..8aef0f7 --- /dev/null +++ b/patches/net/minecraft/block/BlockDragonEgg.java.patch @@ -0,0 +1,44 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockDragonEgg.java ++++ ../src-work/minecraft/net/minecraft/block/BlockDragonEgg.java +@@ -10,6 +10,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.block.BlockFromToEvent; // CraftBukkit ++ + public class BlockDragonEgg extends Block + { + private static final String __OBFID = "CL_00000232"; +@@ -43,7 +45,8 @@ + + if (!BlockFalling.fallInstantly && p_150018_1_.checkChunksExist(p_150018_2_ - b0, p_150018_3_ - b0, p_150018_4_ - b0, p_150018_2_ + b0, p_150018_3_ + b0, p_150018_4_ + b0)) + { +- EntityFallingBlock entityfallingblock = new EntityFallingBlock(p_150018_1_, (double)((float)p_150018_2_ + 0.5F), (double)((float)p_150018_3_ + 0.5F), (double)((float)p_150018_4_ + 0.5F), this); ++ // CraftBukkit - added data ++ EntityFallingBlock entityfallingblock = new EntityFallingBlock(p_150018_1_, (double)((float) p_150018_2_ + 0.5F), (double)((float) p_150018_3_ + 0.5F), (double)((float) p_150018_4_ + 0.5F), this, p_150018_1_.getBlockMetadata(p_150018_2_, p_150018_3_, p_150018_4_)); + p_150018_1_.spawnEntityInWorld(entityfallingblock); + } + else +@@ -86,6 +89,22 @@ + + if (p_150019_1_.getBlock(i1, j1, k1).blockMaterial == Material.air) + { ++ // CraftBukkit start ++ org.bukkit.block.Block from = p_150019_1_.getWorld().getBlockAt(p_150019_2_, p_150019_3_, p_150019_4_); ++ org.bukkit.block.Block to = p_150019_1_.getWorld().getBlockAt(i1, j1, k1); ++ BlockFromToEvent event = new BlockFromToEvent(from, to); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ i1 = event.getToBlock().getX(); ++ j1 = event.getToBlock().getY(); ++ k1 = event.getToBlock().getZ(); ++ // CraftBukkit end ++ + if (!p_150019_1_.isRemote) + { + p_150019_1_.setBlock(i1, j1, k1, this, p_150019_1_.getBlockMetadata(p_150019_2_, p_150019_3_, p_150019_4_), 2); diff --git a/patches/net/minecraft/block/BlockDropper.java.patch b/patches/net/minecraft/block/BlockDropper.java.patch new file mode 100644 index 0000000..5b1842d --- /dev/null +++ b/patches/net/minecraft/block/BlockDropper.java.patch @@ -0,0 +1,59 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockDropper.java ++++ ../src-work/minecraft/net/minecraft/block/BlockDropper.java +@@ -13,6 +13,11 @@ + import net.minecraft.tileentity.TileEntityHopper; + import net.minecraft.util.Facing; + import net.minecraft.world.World; ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.inventory.InventoryMoveItemEvent; ++import net.minecraft.inventory.InventoryLargeChest; ++// CraftBukkit end + + public class BlockDropper extends BlockDispenser + { +@@ -38,7 +43,7 @@ + return new TileEntityDropper(); + } + +- protected void func_149941_e(World p_149941_1_, int p_149941_2_, int p_149941_3_, int p_149941_4_) ++ public void func_149941_e(World p_149941_1_, int p_149941_2_, int p_149941_3_, int p_149941_4_) // CraftBukkit - protected -> public + { + BlockSourceImpl blocksourceimpl = new BlockSourceImpl(p_149941_1_, p_149941_2_, p_149941_3_, p_149941_4_); + TileEntityDispenser tileentitydispenser = (TileEntityDispenser)blocksourceimpl.getBlockTileEntity(); +@@ -60,10 +65,33 @@ + + if (iinventory != null) + { +- itemstack1 = TileEntityHopper.func_145889_a(iinventory, itemstack.copy().splitStack(1), Facing.oppositeSide[i1]); ++ // CraftBukkit start - Fire event when pushing items into other inventories ++ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack.copy().splitStack(1)); ++ org.bukkit.inventory.Inventory destinationInventory; + +- if (itemstack1 == null) ++ // Have to special case large chests as they work oddly ++ if (iinventory instanceof InventoryLargeChest) + { ++ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); ++ } ++ else ++ { ++ destinationInventory = iinventory.getOwner().getInventory(); ++ } ++ ++ InventoryMoveItemEvent event = new InventoryMoveItemEvent(tileentitydispenser.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); ++ p_149941_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ itemstack1 = TileEntityHopper.func_145889_a(iinventory, CraftItemStack.asNMSCopy(event.getItem()), Facing.oppositeSide[i1]); ++ ++ if (event.getItem().equals(oitemstack) && itemstack1 == null) ++ { ++ // CraftBukkit end + itemstack1 = itemstack.copy(); + + if (--itemstack1.stackSize == 0) diff --git a/patches/net/minecraft/block/BlockDynamicLiquid.java.patch b/patches/net/minecraft/block/BlockDynamicLiquid.java.patch new file mode 100644 index 0000000..96360f0 --- /dev/null +++ b/patches/net/minecraft/block/BlockDynamicLiquid.java.patch @@ -0,0 +1,162 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockDynamicLiquid.java ++++ ../src-work/minecraft/net/minecraft/block/BlockDynamicLiquid.java +@@ -5,6 +5,11 @@ + import net.minecraft.init.Blocks; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.block.BlockFace; ++import org.bukkit.event.block.BlockFromToEvent; ++// CraftBukkit end ++ + public class BlockDynamicLiquid extends BlockLiquid + { + int field_149815_a; +@@ -37,6 +42,12 @@ + int i1 = this.tickRate(p_149674_1_); + int j1; + ++ // Cauldron - move CB edit to after variable initialization for coremod compatibility ++ // CraftBukkit start ++ org.bukkit.World bworld = p_149674_1_.getWorld(); ++ org.bukkit.Server server = p_149674_1_.getServer(); ++ org.bukkit.block.Block source = bworld == null ? null : bworld.getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_); ++ // CraftBukkit end + if (l > 0) + { + byte b1 = -100; +@@ -66,17 +77,22 @@ + } + } + +- if (this.field_149815_a >= 2 && this.blockMaterial == Material.water) ++ // Cauldron start - allow disabling infinite water sources ++ if(net.minecraft.server.MinecraftServer.getServer().cauldronConfig.infiniteWaterSource.getValue()) + { +- if (p_149674_1_.getBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_).getMaterial().isSolid()) ++ if (this.field_149815_a >= 2 && this.blockMaterial == Material.water) + { +- j1 = 0; ++ if (p_149674_1_.getBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_).getMaterial().isSolid()) ++ { ++ j1 = 0; ++ } ++ else if (p_149674_1_.getBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_).getMaterial() == this.blockMaterial && p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_ - 1, p_149674_4_) == 0) ++ { ++ j1 = 0; ++ } + } +- else if (p_149674_1_.getBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_).getMaterial() == this.blockMaterial && p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_ - 1, p_149674_4_) == 0) +- { +- j1 = 0; +- } + } ++ // Cauldron end + + if (this.blockMaterial == Material.lava && l < 8 && j1 < 8 && j1 > l && p_149674_5_.nextInt(4) != 0) + { +@@ -89,6 +105,13 @@ + { + this.func_149811_n(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_); + } ++ // Cauldron start - allow lava decaying at a 'natural' rate - see https://mojang.atlassian.net/browse/MC-4631 Lava decay fails to schedule block update ++ else if (net.minecraft.server.MinecraftServer.getServer().cauldronConfig.flowingLavaDecay.getValue()) ++ { ++ // Ensure that even if the flow decay was skipped, it will retry at the material's natural flow period. ++ p_149674_1_.scheduleBlockUpdate(p_149674_2_, p_149674_3_, p_149674_4_, this, this.tickRate(p_149674_1_)); ++ } ++ // Cauldron end + } + else + { +@@ -113,21 +136,34 @@ + + if (this.func_149809_q(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_)) + { +- if (this.blockMaterial == Material.lava && p_149674_1_.getBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_).getMaterial() == Material.water) ++ // CraftBukkit start - Send "down" to the server ++ BlockFromToEvent event = new BlockFromToEvent(source, BlockFace.DOWN); ++ ++ if (server != null) + { +- p_149674_1_.setBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_, Blocks.stone); +- this.func_149799_m(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_); +- return; ++ server.getPluginManager().callEvent(event); + } + +- if (l >= 8) ++ if (!event.isCancelled()) + { +- this.func_149813_h(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_, l); ++ if (this.blockMaterial == Material.lava && p_149674_1_.getBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_).getMaterial() == Material.water) ++ { ++ p_149674_1_.setBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_, Blocks.stone); ++ this.func_149799_m(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_); ++ return; ++ } ++ ++ if (l >= 8) ++ { ++ this.func_149813_h(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_, l); ++ } ++ else ++ { ++ this.func_149813_h(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_, l + 8); ++ } + } +- else +- { +- this.func_149813_h(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_, l + 8); +- } ++ ++ // CraftBukkit end + } + else if (l >= 0 && (l == 0 || this.func_149807_p(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_))) + { +@@ -144,25 +180,31 @@ + return; + } + +- if (aboolean[0]) +- { +- this.func_149813_h(p_149674_1_, p_149674_2_ - 1, p_149674_3_, p_149674_4_, j1); +- } ++ // CraftBukkit start - All four cardinal directions. Do not change the order! ++ BlockFace[] faces = new BlockFace[] { BlockFace.WEST, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH }; ++ int index = 0; + +- if (aboolean[1]) ++ for (BlockFace currentFace : faces) + { +- this.func_149813_h(p_149674_1_, p_149674_2_ + 1, p_149674_3_, p_149674_4_, j1); +- } ++ if (aboolean[index]) ++ { ++ BlockFromToEvent event = new BlockFromToEvent(source, currentFace); + +- if (aboolean[2]) +- { +- this.func_149813_h(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_ - 1, j1); +- } ++ if (server != null) ++ { ++ server.getPluginManager().callEvent(event); ++ } + +- if (aboolean[3]) +- { +- this.func_149813_h(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_ + 1, j1); ++ if (!event.isCancelled()) ++ { ++ this.func_149813_h(p_149674_1_, p_149674_2_ + currentFace.getModX(), p_149674_3_, p_149674_4_ + currentFace.getModZ(), j1); ++ } ++ } ++ ++ index++; + } ++ ++ // CraftBukkit end + } + } + diff --git a/patches/net/minecraft/block/BlockEndPortal.java.patch b/patches/net/minecraft/block/BlockEndPortal.java.patch new file mode 100644 index 0000000..90f7b51 --- /dev/null +++ b/patches/net/minecraft/block/BlockEndPortal.java.patch @@ -0,0 +1,22 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockEndPortal.java ++++ ../src-work/minecraft/net/minecraft/block/BlockEndPortal.java +@@ -15,6 +15,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.EntityPortalEnterEvent; // CraftBukkit ++ + public class BlockEndPortal extends BlockContainer + { + public static boolean field_149948_a; +@@ -64,6 +66,10 @@ + { + if (p_149670_5_.ridingEntity == null && p_149670_5_.riddenByEntity == null && !p_149670_1_.isRemote) + { ++ // CraftBukkit start - Entity in portal ++ EntityPortalEnterEvent event = new EntityPortalEnterEvent(p_149670_5_.getBukkitEntity(), new org.bukkit.Location(p_149670_1_.getWorld(), p_149670_2_, p_149670_3_, p_149670_4_)); ++ p_149670_1_.getServer().getPluginManager().callEvent(event); ++ // CraftBukkit end + p_149670_5_.travelToDimension(1); + } + } diff --git a/patches/net/minecraft/block/BlockFarmland.java.patch b/patches/net/minecraft/block/BlockFarmland.java.patch new file mode 100644 index 0000000..d548dc6 --- /dev/null +++ b/patches/net/minecraft/block/BlockFarmland.java.patch @@ -0,0 +1,57 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockFarmland.java ++++ ../src-work/minecraft/net/minecraft/block/BlockFarmland.java +@@ -15,6 +15,11 @@ + import net.minecraftforge.common.IPlantable; + import net.minecraftforge.common.util.ForgeDirection; + ++// CraftBukkit start ++import org.bukkit.event.entity.EntityInteractEvent; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++// CraftBukkit end ++ + public class BlockFarmland extends Block + { + @SideOnly(Side.CLIENT) +@@ -64,6 +69,15 @@ + } + else if (!this.func_149822_e(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_)) + { ++ // CraftBukkit start ++ org.bukkit.block.Block block = p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_); ++ ++ if (CraftEventFactory.callBlockFadeEvent(block, Blocks.dirt).isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, Blocks.dirt); + } + } +@@ -82,6 +96,26 @@ + return; + } + ++ // CraftBukkit start - Interact soil ++ // Cauldron start - validate data before sending event ++ org.bukkit.event.Cancellable cancellable = null; ++ ++ if (p_149746_5_ instanceof EntityPlayer) ++ { ++ cancellable = CraftEventFactory.callPlayerInteractEvent((EntityPlayer) p_149746_5_, org.bukkit.event.block.Action.PHYSICAL, p_149746_2_, p_149746_3_, p_149746_4_, -1, null); ++ } ++ else if (p_149746_1_ != null && p_149746_1_.getWorld() != null && p_149746_5_ != null) ++ { ++ cancellable = new EntityInteractEvent(p_149746_5_.getBukkitEntity(), p_149746_1_.getWorld().getBlockAt(p_149746_2_, p_149746_3_, p_149746_4_)); ++ p_149746_1_.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); ++ } ++ ++ if (cancellable != null && cancellable.isCancelled()) ++ { ++ return; ++ } ++ // Cauldron end ++ // CraftBukkit end + p_149746_1_.setBlock(p_149746_2_, p_149746_3_, p_149746_4_, Blocks.dirt); + } + } diff --git a/patches/net/minecraft/block/BlockFire.java.patch b/patches/net/minecraft/block/BlockFire.java.patch new file mode 100644 index 0000000..a25bf69 --- /dev/null +++ b/patches/net/minecraft/block/BlockFire.java.patch @@ -0,0 +1,129 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockFire.java ++++ ../src-work/minecraft/net/minecraft/block/BlockFire.java +@@ -17,6 +17,11 @@ + import net.minecraft.world.WorldProviderEnd; + import net.minecraftforge.common.util.ForgeDirection; + import static net.minecraftforge.common.util.ForgeDirection.*; ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.block.BlockBurnEvent; ++import org.bukkit.event.block.BlockSpreadEvent; ++// CraftBukkit end + + public class BlockFire extends Block + { +@@ -105,12 +110,12 @@ + + if (!this.canPlaceBlockAt(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_)) + { +- p_149674_1_.setBlockToAir(p_149674_2_, p_149674_3_, p_149674_4_); ++ fireExtinguished(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_); // CraftBukkit - invalid place location + } + + if (!flag && p_149674_1_.isRaining() && (p_149674_1_.canLightningStrikeAt(p_149674_2_, p_149674_3_, p_149674_4_) || p_149674_1_.canLightningStrikeAt(p_149674_2_ - 1, p_149674_3_, p_149674_4_) || p_149674_1_.canLightningStrikeAt(p_149674_2_ + 1, p_149674_3_, p_149674_4_) || p_149674_1_.canLightningStrikeAt(p_149674_2_, p_149674_3_, p_149674_4_ - 1) || p_149674_1_.canLightningStrikeAt(p_149674_2_, p_149674_3_, p_149674_4_ + 1))) + { +- p_149674_1_.setBlockToAir(p_149674_2_, p_149674_3_, p_149674_4_); ++ fireExtinguished(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_); // CraftBukkit - extinguished by rain + } + else + { +@@ -127,12 +132,12 @@ + { + if (!World.doesBlockHaveSolidTopSurface(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_) || l > 3) + { +- p_149674_1_.setBlockToAir(p_149674_2_, p_149674_3_, p_149674_4_); ++ fireExtinguished(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_); // CraftBukkit - burn out of inflammable block + } + } + else if (!flag && !this.canCatchFire(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_, UP) && l == 15 && p_149674_5_.nextInt(4) == 0) + { +- p_149674_1_.setBlockToAir(p_149674_2_, p_149674_3_, p_149674_4_); ++ fireExtinguished(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_); // CraftBukkit - burn out + } + else + { +@@ -186,7 +191,29 @@ + k2 = 15; + } + +- p_149674_1_.setBlock(i1, k1, j1, this, k2, 3); ++ // CraftBukkit start - Call to stop spread of fire ++ if (p_149674_1_.getBlock(i1, k1, j1) != Blocks.fire) ++ { ++ if (CraftEventFactory.callBlockIgniteEvent(p_149674_1_, i1, k1, j1, p_149674_2_, p_149674_3_, p_149674_4_).isCancelled()) ++ { ++ continue; ++ } ++ ++ org.bukkit.Server server = p_149674_1_.getServer(); ++ org.bukkit.World bworld = p_149674_1_.getWorld(); ++ org.bukkit.block.BlockState blockState = bworld.getBlockAt(i1, k1, j1).getState(); ++ blockState.setTypeId(Block.getIdFromBlock(this)); ++ blockState.setData(new org.bukkit.material.MaterialData(Block.getIdFromBlock(this), (byte) k2)); ++ BlockSpreadEvent spreadEvent = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_), blockState); ++ server.getPluginManager().callEvent(spreadEvent); ++ ++ if (!spreadEvent.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ } ++ ++ // CraftBukkit end + } + } + } +@@ -216,7 +243,18 @@ + if (p_149841_6_.nextInt(p_149841_5_) < j1) + { + boolean flag = p_149841_1_.getBlock(p_149841_2_, p_149841_3_, p_149841_4_) == Blocks.tnt; ++ // CraftBukkit start ++ org.bukkit.block.Block theBlock = p_149841_1_.getWorld().getBlockAt(p_149841_2_, p_149841_3_, p_149841_4_); ++ BlockBurnEvent event = new BlockBurnEvent(theBlock); ++ p_149841_1_.getServer().getPluginManager().callEvent(event); + ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end ++ + if (p_149841_6_.nextInt(p_149841_7_ + 10) < 5 && !p_149841_1_.canLightningStrikeAt(p_149841_2_, p_149841_3_, p_149841_4_)) + { + int k1 = p_149841_7_ + p_149841_6_.nextInt(5) / 4; +@@ -297,7 +335,7 @@ + { + if (!World.doesBlockHaveSolidTopSurface(p_149695_1_, p_149695_2_, p_149695_3_ - 1, p_149695_4_) && !this.canNeighborBurn(p_149695_1_, p_149695_2_, p_149695_3_, p_149695_4_)) + { +- p_149695_1_.setBlockToAir(p_149695_2_, p_149695_3_, p_149695_4_); ++ fireExtinguished(p_149695_1_, p_149695_2_, p_149695_3_, p_149695_4_); // CraftBukkit - fuel block gone + } + } + +@@ -307,7 +345,7 @@ + { + if (!World.doesBlockHaveSolidTopSurface(p_149726_1_, p_149726_2_, p_149726_3_ - 1, p_149726_4_) && !this.canNeighborBurn(p_149726_1_, p_149726_2_, p_149726_3_, p_149726_4_)) + { +- p_149726_1_.setBlockToAir(p_149726_2_, p_149726_3_, p_149726_4_); ++ fireExtinguished(p_149726_1_, p_149726_2_, p_149726_3_, p_149726_4_); // CraftBukkit - fuel block broke + } + else + { +@@ -398,6 +436,16 @@ + } + } + ++ // CraftBukkit start ++ private void fireExtinguished(World world, int x, int y, int z) ++ { ++ if (!CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(x, y, z), Blocks.air).isCancelled()) ++ { ++ world.setBlockToAir(x, y, z); ++ } ++ } ++ // CraftBukkit end ++ + @SideOnly(Side.CLIENT) + public void registerBlockIcons(IIconRegister p_149651_1_) + { diff --git a/patches/net/minecraft/block/BlockGrass.java.patch b/patches/net/minecraft/block/BlockGrass.java.patch new file mode 100644 index 0000000..9759c5d --- /dev/null +++ b/patches/net/minecraft/block/BlockGrass.java.patch @@ -0,0 +1,64 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockGrass.java ++++ ../src-work/minecraft/net/minecraft/block/BlockGrass.java +@@ -15,6 +15,12 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import org.bukkit.block.BlockState; ++import org.bukkit.event.block.BlockSpreadEvent; ++import org.bukkit.event.block.BlockFadeEvent; ++// CraftBukkit end ++ + public class BlockGrass extends Block implements IGrowable + { + private static final Logger logger = LogManager.getLogger(); +@@ -45,11 +51,25 @@ + { + if (p_149674_1_.getBlockLightValue(p_149674_2_, p_149674_3_ + 1, p_149674_4_) < 4 && p_149674_1_.getBlockLightOpacity(p_149674_2_, p_149674_3_ + 1, p_149674_4_) > 2) + { +- p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, Blocks.dirt); ++ // CraftBukkit start ++ org.bukkit.World bworld = p_149674_1_.getWorld(); ++ BlockState blockState = bworld.getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_).getState(); ++ blockState.setTypeId(Block.getIdFromBlock(Blocks.dirt)); ++ BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState); ++ p_149674_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ ++ // CraftBukkit end + } + else if (p_149674_1_.getBlockLightValue(p_149674_2_, p_149674_3_ + 1, p_149674_4_) >= 9) + { +- for (int l = 0; l < 4; ++l) ++ int numGrowth = Math.min(4, Math.max(20, (int)(4 * 100F / p_149674_1_.growthOdds))); // Spigot ++ ++ for (int l = 0; l < numGrowth; ++l) // Spigot + { + int i1 = p_149674_2_ + p_149674_5_.nextInt(3) - 1; + int j1 = p_149674_3_ + p_149674_5_.nextInt(5) - 3; +@@ -58,7 +78,19 @@ + + if (p_149674_1_.getBlock(i1, j1, k1) == Blocks.dirt && p_149674_1_.getBlockMetadata(i1, j1, k1) == 0 && p_149674_1_.getBlockLightValue(i1, j1 + 1, k1) >= 4 && p_149674_1_.getBlockLightOpacity(i1, j1 + 1, k1) <= 2) + { +- p_149674_1_.setBlock(i1, j1, k1, Blocks.grass); ++ // CraftBukkit start ++ org.bukkit.World bworld = p_149674_1_.getWorld(); ++ BlockState blockState = bworld.getBlockAt(i1, j1, k1).getState(); ++ blockState.setTypeId(Block.getIdFromBlock(Blocks.grass)); ++ BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_), blockState); ++ p_149674_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ ++ // CraftBukkit end + } + } + } diff --git a/patches/net/minecraft/block/BlockHopper.java.patch b/patches/net/minecraft/block/BlockHopper.java.patch new file mode 100644 index 0000000..265bffe --- /dev/null +++ b/patches/net/minecraft/block/BlockHopper.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockHopper.java ++++ ../src-work/minecraft/net/minecraft/block/BlockHopper.java +@@ -22,6 +22,7 @@ + import net.minecraft.util.IIcon; + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; ++import net.minecraft.inventory.IInventory; // CraftBukkit + + public class BlockHopper extends BlockContainer + { +@@ -210,7 +211,7 @@ + + public static int getDirectionFromMetadata(int p_149918_0_) + { +- return p_149918_0_ & 7; ++ return Math.min(p_149918_0_ & 7, 5); // CraftBukkit - Fix AIOOBE in callers + } + + public static boolean func_149917_c(int p_149917_0_) diff --git a/patches/net/minecraft/block/BlockIce.java.patch b/patches/net/minecraft/block/BlockIce.java.patch new file mode 100644 index 0000000..f710f5b --- /dev/null +++ b/patches/net/minecraft/block/BlockIce.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockIce.java ++++ ../src-work/minecraft/net/minecraft/block/BlockIce.java +@@ -86,6 +86,14 @@ + { + if (p_149674_1_.getSavedLightValue(EnumSkyBlock.Block, p_149674_2_, p_149674_3_, p_149674_4_) > 11 - this.getLightOpacity()) + { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_), Blocks.water).isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end ++ + if (p_149674_1_.provider.isHellWorld) + { + p_149674_1_.setBlockToAir(p_149674_2_, p_149674_3_, p_149674_4_); diff --git a/patches/net/minecraft/block/BlockJukebox.java.patch b/patches/net/minecraft/block/BlockJukebox.java.patch new file mode 100644 index 0000000..4080aec --- /dev/null +++ b/patches/net/minecraft/block/BlockJukebox.java.patch @@ -0,0 +1,26 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockJukebox.java ++++ ../src-work/minecraft/net/minecraft/block/BlockJukebox.java +@@ -163,8 +163,23 @@ + + public void func_145857_a(ItemStack p_145857_1_) + { ++ // CraftBukkit start - There can only be one ++ if (p_145857_1_ != null) ++ { ++ p_145857_1_.stackSize = 1; ++ } ++ ++ // CraftBukkit end + this.field_145858_a = p_145857_1_; + this.markDirty(); + } ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } + } diff --git a/patches/net/minecraft/block/BlockLeaves.java.patch b/patches/net/minecraft/block/BlockLeaves.java.patch new file mode 100644 index 0000000..9e9d924 --- /dev/null +++ b/patches/net/minecraft/block/BlockLeaves.java.patch @@ -0,0 +1,28 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockLeaves.java ++++ ../src-work/minecraft/net/minecraft/block/BlockLeaves.java +@@ -18,6 +18,8 @@ + import net.minecraft.world.World; + import net.minecraftforge.common.IShearable; + ++import org.bukkit.event.block.LeavesDecayEvent; // CraftBukkit ++ + public abstract class BlockLeaves extends BlockLeavesBase implements IShearable + { + int[] field_150128_a; +@@ -222,6 +224,16 @@ + + private void removeLeaves(World p_150126_1_, int p_150126_2_, int p_150126_3_, int p_150126_4_) + { ++ // CraftBukkit start ++ LeavesDecayEvent event = new LeavesDecayEvent(p_150126_1_.getWorld().getBlockAt(p_150126_2_, p_150126_3_, p_150126_4_)); ++ p_150126_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.dropBlockAsItem(p_150126_1_, p_150126_2_, p_150126_3_, p_150126_4_, p_150126_1_.getBlockMetadata(p_150126_2_, p_150126_3_, p_150126_4_), 0); + p_150126_1_.setBlockToAir(p_150126_2_, p_150126_3_, p_150126_4_); + } diff --git a/patches/net/minecraft/block/BlockLever.java.patch b/patches/net/minecraft/block/BlockLever.java.patch new file mode 100644 index 0000000..5e79d6a --- /dev/null +++ b/patches/net/minecraft/block/BlockLever.java.patch @@ -0,0 +1,31 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockLever.java ++++ ../src-work/minecraft/net/minecraft/block/BlockLever.java +@@ -13,6 +13,8 @@ + import net.minecraftforge.common.util.ForgeDirection; + import static net.minecraftforge.common.util.ForgeDirection.*; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockLever extends Block + { + private static final String __OBFID = "CL_00000264"; +@@ -270,6 +272,19 @@ + int i1 = p_149727_1_.getBlockMetadata(p_149727_2_, p_149727_3_, p_149727_4_); + int j1 = i1 & 7; + int k1 = 8 - (i1 & 8); ++ // CraftBukkit start - Interact Lever ++ org.bukkit.block.Block block = p_149727_1_.getWorld().getBlockAt(p_149727_2_, p_149727_3_, p_149727_4_); ++ int old = (k1 != 8) ? 15 : 0; ++ int current = (k1 == 8) ? 15 : 0; ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); ++ p_149727_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if ((eventRedstone.getNewCurrent() > 0) != (k1 == 8)) ++ { ++ return true; ++ } ++ ++ // CraftBukkit end + p_149727_1_.setBlockMetadataWithNotify(p_149727_2_, p_149727_3_, p_149727_4_, j1 + k1, 3); + p_149727_1_.playSoundEffect((double)p_149727_2_ + 0.5D, (double)p_149727_3_ + 0.5D, (double)p_149727_4_ + 0.5D, "random.click", 0.3F, k1 > 0 ? 0.6F : 0.5F); + p_149727_1_.notifyBlocksOfNeighborChange(p_149727_2_, p_149727_3_, p_149727_4_, this); diff --git a/patches/net/minecraft/block/BlockMushroom.java.patch b/patches/net/minecraft/block/BlockMushroom.java.patch new file mode 100644 index 0000000..c494ff1 --- /dev/null +++ b/patches/net/minecraft/block/BlockMushroom.java.patch @@ -0,0 +1,60 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockMushroom.java ++++ ../src-work/minecraft/net/minecraft/block/BlockMushroom.java +@@ -6,6 +6,12 @@ + import net.minecraft.world.gen.feature.WorldGenBigMushroom; + import net.minecraftforge.common.util.ForgeDirection; + ++// CraftBukkit start ++import org.bukkit.block.BlockState; ++import org.bukkit.event.block.BlockSpreadEvent; ++import org.bukkit.TreeType; ++// CraftBukkit end ++ + public class BlockMushroom extends BlockBush implements IGrowable + { + private static final String __OBFID = "CL_00000272"; +@@ -19,7 +25,9 @@ + + public void updateTick(World p_149674_1_, int p_149674_2_, int p_149674_3_, int p_149674_4_, Random p_149674_5_) + { +- if (p_149674_5_.nextInt(25) == 0) ++ final int sourceX = p_149674_2_, sourceY = p_149674_3_, sourceZ = p_149674_4_; // CraftBukkit ++ ++ if (p_149674_5_.nextInt(Math.max(1, (int) p_149674_1_.growthOdds / p_149674_1_.getSpigotConfig().mushroomModifier * 25)) == 0) // Spigot // Cauldron + { + byte b0 = 4; + int l = 5; +@@ -66,7 +74,19 @@ + + if (p_149674_1_.isAirBlock(i1, j1, k1) && this.canBlockStay(p_149674_1_, i1, j1, k1)) + { +- p_149674_1_.setBlock(i1, j1, k1, this, 0, 2); ++ // CraftBukkit start ++ org.bukkit.World bworld = p_149674_1_.getWorld(); ++ BlockState blockState = bworld.getBlockAt(i1, j1, k1).getState(); ++ blockState.setTypeId(Block.getIdFromBlock(this)); // nms: this.id, 0, 2 ++ BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(sourceX, sourceY, sourceZ), blockState); ++ p_149674_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ ++ // CraftBukkit end + } + } + } +@@ -102,10 +122,12 @@ + + if (this == Blocks.brown_mushroom) + { ++ BlockSapling.treeType = TreeType.BROWN_MUSHROOM; // CraftBukkit + worldgenbigmushroom = new WorldGenBigMushroom(0); + } + else if (this == Blocks.red_mushroom) + { ++ BlockSapling.treeType = TreeType.RED_MUSHROOM; // CraftBukkit + worldgenbigmushroom = new WorldGenBigMushroom(1); + } + diff --git a/patches/net/minecraft/block/BlockMycelium.java.patch b/patches/net/minecraft/block/BlockMycelium.java.patch new file mode 100644 index 0000000..3aa46df --- /dev/null +++ b/patches/net/minecraft/block/BlockMycelium.java.patch @@ -0,0 +1,64 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockMycelium.java ++++ ../src-work/minecraft/net/minecraft/block/BlockMycelium.java +@@ -12,6 +12,12 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.block.BlockState; ++import org.bukkit.event.block.BlockFadeEvent; ++import org.bukkit.event.block.BlockSpreadEvent; ++// CraftBukkit end ++ + public class BlockMycelium extends Block + { + @SideOnly(Side.CLIENT) +@@ -39,11 +45,25 @@ + { + if (p_149674_1_.getBlockLightValue(p_149674_2_, p_149674_3_ + 1, p_149674_4_) < 4 && p_149674_1_.getBlockLightOpacity(p_149674_2_, p_149674_3_ + 1, p_149674_4_) > 2) + { +- p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, Blocks.dirt); ++ // CraftBukkit start ++ org.bukkit.World bworld = p_149674_1_.getWorld(); ++ BlockState blockState = bworld.getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_).getState(); ++ blockState.setTypeId(Block.getIdFromBlock(Blocks.dirt)); ++ BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState); ++ p_149674_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ ++ // CraftBukkit end + } + else if (p_149674_1_.getBlockLightValue(p_149674_2_, p_149674_3_ + 1, p_149674_4_) >= 9) + { +- for (int l = 0; l < 4; ++l) ++ int numGrowth = Math.min(4, Math.max(20, (int)(4 * 100F / p_149674_1_.growthOdds))); // Spigot ++ ++ for (int l = 0; l < numGrowth; ++l) // Spigot + { + int i1 = p_149674_2_ + p_149674_5_.nextInt(3) - 1; + int j1 = p_149674_3_ + p_149674_5_.nextInt(5) - 3; +@@ -52,7 +72,19 @@ + + if (p_149674_1_.getBlock(i1, j1, k1) == Blocks.dirt && p_149674_1_.getBlockMetadata(i1, j1, k1) == 0 && p_149674_1_.getBlockLightValue(i1, j1 + 1, k1) >= 4 && p_149674_1_.getBlockLightOpacity(i1, j1 + 1, k1) <= 2) + { +- p_149674_1_.setBlock(i1, j1, k1, this); ++ // CraftBukkit start ++ org.bukkit.World bworld = p_149674_1_.getWorld(); ++ BlockState blockState = bworld.getBlockAt(i1, j1, k1).getState(); ++ blockState.setTypeId(Block.getIdFromBlock(this)); ++ BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_), blockState); ++ p_149674_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ ++ // CraftBukkit end + } + } + } diff --git a/patches/net/minecraft/block/BlockNetherWart.java.patch b/patches/net/minecraft/block/BlockNetherWart.java.patch new file mode 100644 index 0000000..67b7fdd --- /dev/null +++ b/patches/net/minecraft/block/BlockNetherWart.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockNetherWart.java ++++ ../src-work/minecraft/net/minecraft/block/BlockNetherWart.java +@@ -45,7 +45,7 @@ + if (l < 3 && p_149674_5_.nextInt(10) == 0) + { + ++l; +- p_149674_1_.setBlockMetadataWithNotify(p_149674_2_, p_149674_3_, p_149674_4_, l, 2); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, this, l); // CraftBukkit + } + + super.updateTick(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_5_); diff --git a/patches/net/minecraft/block/BlockNetherrack.java.patch b/patches/net/minecraft/block/BlockNetherrack.java.patch new file mode 100644 index 0000000..017cd12 --- /dev/null +++ b/patches/net/minecraft/block/BlockNetherrack.java.patch @@ -0,0 +1,32 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockNetherrack.java ++++ ../src-work/minecraft/net/minecraft/block/BlockNetherrack.java +@@ -4,6 +4,11 @@ + import net.minecraft.block.material.Material; + import net.minecraft.creativetab.CreativeTabs; + ++// CraftBukkit start ++import org.bukkit.event.block.BlockRedstoneEvent; ++import net.minecraft.world.World; ++// CraftBukkit end ++ + public class BlockNetherrack extends Block + { + private static final String __OBFID = "CL_00000275"; +@@ -18,4 +23,17 @@ + { + return MapColor.netherrackColor; + } ++ ++ // CraftBukkit start ++ public void doPhysics(World world, int i, int j, int k, int l) ++ { ++ if (Block.getBlockById(l) != null && Block.getBlockById(l).canProvidePower()) ++ { ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); ++ int power = block.getBlockPower(); ++ BlockRedstoneEvent event = new BlockRedstoneEvent(block, power, power); ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/block/BlockPistonBase.java.patch b/patches/net/minecraft/block/BlockPistonBase.java.patch new file mode 100644 index 0000000..1f2b07d --- /dev/null +++ b/patches/net/minecraft/block/BlockPistonBase.java.patch @@ -0,0 +1,116 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockPistonBase.java ++++ ../src-work/minecraft/net/minecraft/block/BlockPistonBase.java +@@ -21,6 +21,12 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.block.CraftBlock; ++import org.bukkit.event.block.BlockPistonRetractEvent; ++import org.bukkit.event.block.BlockPistonExtendEvent; ++// CraftBukkit end ++ + public class BlockPistonBase extends Block + { + private final boolean isSticky; +@@ -128,13 +134,37 @@ + + if (flag && !isExtended(l)) + { +- if (canExtend(p_150078_1_, p_150078_2_, p_150078_3_, p_150078_4_, i1)) ++ // CraftBukkit start ++ int length = canExtend_IntCB(p_150078_1_, p_150078_2_, p_150078_3_, p_150078_4_, i1); ++ ++ if (length >= 0) + { ++ org.bukkit.block.Block block = p_150078_1_.getWorld().getBlockAt(p_150078_2_, p_150078_3_, p_150078_4_); ++ BlockPistonExtendEvent event = new BlockPistonExtendEvent(block, length, CraftBlock.notchToBlockFace(i1)); ++ p_150078_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + p_150078_1_.addBlockEvent(p_150078_2_, p_150078_3_, p_150078_4_, this, 0, i1); + } + } + else if (!flag && isExtended(l)) + { ++ // CraftBukkit start ++ org.bukkit.block.Block block = p_150078_1_.getWorld().getBlockAt(p_150078_2_, p_150078_3_, p_150078_4_); ++ BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, CraftBlock.notchToBlockFace(i1)); ++ p_150078_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + p_150078_1_.setBlockMetadataWithNotify(p_150078_2_, p_150078_3_, p_150078_4_, i1, 2); + p_150078_1_.addBlockEvent(p_150078_2_, p_150078_3_, p_150078_4_, this, 1, i1); + } +@@ -297,6 +327,11 @@ + + public static int getPistonOrientation(int p_150076_0_) + { ++ if ((p_150076_0_ & 7) >= Facing.oppositeSide.length) ++ { ++ return 7; // CraftBukkit - check for AIOOB on piston data ++ } ++ + return p_150076_0_ & 7; + } + +@@ -366,7 +401,13 @@ + } + } + +- private static boolean canExtend(World p_150077_0_, int p_150077_1_, int p_150077_2_, int p_150077_3_, int p_150077_4_) ++ // Cauldron start - vanilla compatibility ++ private static boolean canExtend(World world, int i, int j, int k, int l) { ++ return canExtend_IntCB(world, i, j, k, l) >= 0; ++ } ++ // Cauldron end ++ ++ private static int canExtend_IntCB(World p_150077_0_, int p_150077_1_, int p_150077_2_, int p_150077_3_, int p_150077_4_) // CraftBukkit int -> boolean + { + int i1 = p_150077_1_ + Facing.offsetsXForSide[p_150077_4_]; + int j1 = p_150077_2_ + Facing.offsetsYForSide[p_150077_4_]; +@@ -379,7 +420,7 @@ + { + if (j1 <= 0 || j1 >= p_150077_0_.getHeight()) + { +- return false; ++ return -1; // CraftBukkit + } + + Block block = p_150077_0_.getBlock(i1, j1, k1); +@@ -388,14 +429,14 @@ + { + if (!canPushBlock(block, p_150077_0_, i1, j1, k1, true)) + { +- return false; ++ return -1; // CraftBukkit + } + + if (block.getMobilityFlag() != 1) + { + if (l1 == 12) + { +- return false; ++ return -1; // CraftBukkit + } + + i1 += Facing.offsetsXForSide[p_150077_4_]; +@@ -407,7 +448,7 @@ + } + } + +- return true; ++ return l1; // CraftBukkit + } + } + diff --git a/patches/net/minecraft/block/BlockPistonExtension.java.patch b/patches/net/minecraft/block/BlockPistonExtension.java.patch new file mode 100644 index 0000000..9fb7b66 --- /dev/null +++ b/patches/net/minecraft/block/BlockPistonExtension.java.patch @@ -0,0 +1,28 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockPistonExtension.java ++++ ../src-work/minecraft/net/minecraft/block/BlockPistonExtension.java +@@ -55,6 +55,12 @@ + public void breakBlock(World p_149749_1_, int p_149749_2_, int p_149749_3_, int p_149749_4_, Block p_149749_5_, int p_149749_6_) + { + super.breakBlock(p_149749_1_, p_149749_2_, p_149749_3_, p_149749_4_, p_149749_5_, p_149749_6_); ++ ++ if ((p_149749_6_ & 7) >= Facing.oppositeSide.length) ++ { ++ return; // CraftBukkit - fix a piston AIOOBE issue ++ } ++ + int i1 = Facing.oppositeSide[getDirectionMeta(p_149749_6_)]; + p_149749_2_ += Facing.offsetsXForSide[i1]; + p_149749_3_ += Facing.offsetsYForSide[i1]; +@@ -200,6 +206,12 @@ + public void onNeighborBlockChange(World p_149695_1_, int p_149695_2_, int p_149695_3_, int p_149695_4_, Block p_149695_5_) + { + int l = getDirectionMeta(p_149695_1_.getBlockMetadata(p_149695_2_, p_149695_3_, p_149695_4_)); ++ ++ if ((l & 7) >= Facing.oppositeSide.length) ++ { ++ return; // CraftBukkit - fix a piston AIOOBE issue ++ } ++ + Block block1 = p_149695_1_.getBlock(p_149695_2_ - Facing.offsetsXForSide[l], p_149695_3_ - Facing.offsetsYForSide[l], p_149695_4_ - Facing.offsetsZForSide[l]); + + if (block1 != Blocks.piston && block1 != Blocks.sticky_piston) diff --git a/patches/net/minecraft/block/BlockPortal.java.patch b/patches/net/minecraft/block/BlockPortal.java.patch new file mode 100644 index 0000000..392c626 --- /dev/null +++ b/patches/net/minecraft/block/BlockPortal.java.patch @@ -0,0 +1,383 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockPortal.java ++++ ../src-work/minecraft/net/minecraft/block/BlockPortal.java +@@ -14,6 +14,11 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.event.entity.EntityPortalEnterEvent; ++import org.bukkit.event.world.PortalCreateEvent; ++// CraftBukkit end ++ + public class BlockPortal extends BlockBreakable + { + public static final int[][] field_150001_a = new int[][] {new int[0], {3, 1}, {2, 0}}; +@@ -29,7 +34,7 @@ + { + super.updateTick(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_5_); + +- if (p_149674_1_.provider.isSurfaceWorld() && p_149674_1_.getGameRules().getGameRuleBooleanValue("doMobSpawning") && p_149674_5_.nextInt(2000) < p_149674_1_.difficultySetting.getDifficultyId()) ++ if (p_149674_1_.getSpigotConfig().enableZombiePigmenPortalSpawns && p_149674_1_.provider.isSurfaceWorld() && p_149674_1_.getGameRules().getGameRuleBooleanValue("doMobSpawning") && p_149674_5_.nextInt(2000) < p_149674_1_.difficultySetting.getDifficultyId()) // Spigot // Cauldron + { + int l; + +@@ -104,13 +109,15 @@ + + if (size.func_150860_b() && size.field_150864_e == 0) + { +- size.func_150859_c(); +- return true; ++ // CraftBukkit start - return portalcreator ++ return size.CB_func_150859_c(); // Cauldron ++ //return true; + } + else if (size1.func_150860_b() && size1.field_150864_e == 0) + { +- size1.func_150859_c(); +- return true; ++ return size1.CB_func_150859_c(); // Cauldron ++ //return true; ++ // CraftBukkit end + } + else + { +@@ -181,6 +188,10 @@ + { + if (p_149670_5_.ridingEntity == null && p_149670_5_.riddenByEntity == null) + { ++ // CraftBukkit start - Entity in portal ++ EntityPortalEnterEvent event = new EntityPortalEnterEvent(p_149670_5_.getBukkitEntity(), new org.bukkit.Location(p_149670_1_.getWorld(), p_149670_2_, p_149670_3_, p_149670_4_)); ++ p_149670_1_.getServer().getPluginManager().callEvent(event); ++ // CraftBukkit end + p_149670_5_.setInPortal(); + } + } +@@ -239,175 +250,223 @@ + } + + public static class Size ++ { ++ private final World field_150867_a; ++ private final int field_150865_b; ++ private final int field_150866_c; ++ private final int field_150863_d; ++ private int field_150864_e = 0; ++ private ChunkCoordinates field_150861_f; ++ private int field_150862_g; ++ private int field_150868_h; ++ java.util.Collection blocks; // CraftBukkit ++ private static final String __OBFID = "CL_00000285"; ++ ++ public Size(World p_i45415_1_, int p_i45415_2_, int p_i45415_3_, int p_i45415_4_, int p_i45415_5_) + { +- private final World field_150867_a; +- private final int field_150865_b; +- private final int field_150866_c; +- private final int field_150863_d; +- private int field_150864_e = 0; +- private ChunkCoordinates field_150861_f; +- private int field_150862_g; +- private int field_150868_h; +- private static final String __OBFID = "CL_00000285"; ++ this.field_150867_a = p_i45415_1_; ++ this.field_150865_b = p_i45415_5_; ++ this.field_150863_d = BlockPortal.field_150001_a[p_i45415_5_][0]; ++ this.field_150866_c = BlockPortal.field_150001_a[p_i45415_5_][1]; + +- public Size(World p_i45415_1_, int p_i45415_2_, int p_i45415_3_, int p_i45415_4_, int p_i45415_5_) ++ for (int i1 = p_i45415_3_; p_i45415_3_ > i1 - 21 && p_i45415_3_ > 0 && this.func_150857_a(p_i45415_1_.getBlock(p_i45415_2_, p_i45415_3_ - 1, p_i45415_4_)); --p_i45415_3_) + { +- this.field_150867_a = p_i45415_1_; +- this.field_150865_b = p_i45415_5_; +- this.field_150863_d = BlockPortal.field_150001_a[p_i45415_5_][0]; +- this.field_150866_c = BlockPortal.field_150001_a[p_i45415_5_][1]; ++ ; ++ } + +- for (int i1 = p_i45415_3_; p_i45415_3_ > i1 - 21 && p_i45415_3_ > 0 && this.func_150857_a(p_i45415_1_.getBlock(p_i45415_2_, p_i45415_3_ - 1, p_i45415_4_)); --p_i45415_3_) ++ int j1 = this.func_150853_a(p_i45415_2_, p_i45415_3_, p_i45415_4_, this.field_150863_d) - 1; ++ ++ if (j1 >= 0) ++ { ++ this.field_150861_f = new ChunkCoordinates(p_i45415_2_ + j1 * Direction.offsetX[this.field_150863_d], p_i45415_3_, p_i45415_4_ + j1 * Direction.offsetZ[this.field_150863_d]); ++ this.field_150868_h = this.func_150853_a(this.field_150861_f.posX, this.field_150861_f.posY, this.field_150861_f.posZ, this.field_150866_c); ++ ++ if (this.field_150868_h < 2 || this.field_150868_h > 21) + { +- ; ++ this.field_150861_f = null; ++ this.field_150868_h = 0; + } ++ } + +- int j1 = this.func_150853_a(p_i45415_2_, p_i45415_3_, p_i45415_4_, this.field_150863_d) - 1; ++ if (this.field_150861_f != null) ++ { ++ this.field_150862_g = this.func_150858_a(); ++ } ++ } + +- if (j1 >= 0) +- { +- this.field_150861_f = new ChunkCoordinates(p_i45415_2_ + j1 * Direction.offsetX[this.field_150863_d], p_i45415_3_, p_i45415_4_ + j1 * Direction.offsetZ[this.field_150863_d]); +- this.field_150868_h = this.func_150853_a(this.field_150861_f.posX, this.field_150861_f.posY, this.field_150861_f.posZ, this.field_150866_c); ++ protected int func_150853_a(int p_150853_1_, int p_150853_2_, int p_150853_3_, int p_150853_4_) ++ { ++ int j1 = Direction.offsetX[p_150853_4_]; ++ int k1 = Direction.offsetZ[p_150853_4_]; ++ int i1; ++ Block block; + +- if (this.field_150868_h < 2 || this.field_150868_h > 21) +- { +- this.field_150861_f = null; +- this.field_150868_h = 0; +- } ++ for (i1 = 0; i1 < 22; ++i1) ++ { ++ block = this.field_150867_a.getBlock(p_150853_1_ + j1 * i1, p_150853_2_, p_150853_3_ + k1 * i1); ++ ++ if (!this.func_150857_a(block)) ++ { ++ break; + } + +- if (this.field_150861_f != null) ++ Block block1 = this.field_150867_a.getBlock(p_150853_1_ + j1 * i1, p_150853_2_ - 1, p_150853_3_ + k1 * i1); ++ ++ if (block1 != Blocks.obsidian) + { +- this.field_150862_g = this.func_150858_a(); ++ break; + } + } + +- protected int func_150853_a(int p_150853_1_, int p_150853_2_, int p_150853_3_, int p_150853_4_) ++ block = this.field_150867_a.getBlock(p_150853_1_ + j1 * i1, p_150853_2_, p_150853_3_ + k1 * i1); ++ return block == Blocks.obsidian ? i1 : 0; ++ } ++ ++ protected int func_150858_a() ++ { ++ this.blocks = new java.util.HashSet(); // CraftBukkit ++ org.bukkit.World bworld = this.field_150867_a.getWorld(); ++ int i; ++ int j; ++ int k; ++ int l; ++ label56: ++ ++ for (this.field_150862_g = 0; this.field_150862_g < 21; ++this.field_150862_g) + { +- int j1 = Direction.offsetX[p_150853_4_]; +- int k1 = Direction.offsetZ[p_150853_4_]; +- int i1; +- Block block; ++ i = this.field_150861_f.posY + this.field_150862_g; + +- for (i1 = 0; i1 < 22; ++i1) ++ for (j = 0; j < this.field_150868_h; ++j) + { +- block = this.field_150867_a.getBlock(p_150853_1_ + j1 * i1, p_150853_2_, p_150853_3_ + k1 * i1); ++ k = this.field_150861_f.posX + j * Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][1]]; ++ l = this.field_150861_f.posZ + j * Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][1]]; ++ Block block = this.field_150867_a.getBlock(k, i, l); + + if (!this.func_150857_a(block)) + { +- break; ++ break label56; + } + +- Block block1 = this.field_150867_a.getBlock(p_150853_1_ + j1 * i1, p_150853_2_ - 1, p_150853_3_ + k1 * i1); +- +- if (block1 != Blocks.obsidian) ++ if (block == Blocks.portal) + { +- break; ++ ++this.field_150864_e; + } +- } + +- block = this.field_150867_a.getBlock(p_150853_1_ + j1 * i1, p_150853_2_, p_150853_3_ + k1 * i1); +- return block == Blocks.obsidian ? i1 : 0; +- } +- +- protected int func_150858_a() +- { +- int i; +- int j; +- int k; +- int l; +- label56: +- +- for (this.field_150862_g = 0; this.field_150862_g < 21; ++this.field_150862_g) +- { +- i = this.field_150861_f.posY + this.field_150862_g; +- +- for (j = 0; j < this.field_150868_h; ++j) ++ if (j == 0) + { +- k = this.field_150861_f.posX + j * Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][1]]; +- l = this.field_150861_f.posZ + j * Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][1]]; +- Block block = this.field_150867_a.getBlock(k, i, l); ++ block = this.field_150867_a.getBlock(k + Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][0]], i, l + Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][0]]); + +- if (!this.func_150857_a(block)) ++ if (block != Blocks.obsidian) + { + break label56; ++ // CraftBukkit start - add the block to our list + } +- +- if (block == Blocks.portal) ++ else + { +- ++this.field_150864_e; ++ blocks.add(bworld.getBlockAt(k + Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][0]], i, l + Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][0]])); ++ // CraftBukkit end + } ++ } ++ else if (j == this.field_150868_h - 1) ++ { ++ block = this.field_150867_a.getBlock(k + Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][1]], i, l + Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][1]]); + +- if (j == 0) ++ if (block != Blocks.obsidian) + { +- block = this.field_150867_a.getBlock(k + Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][0]], i, l + Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][0]]); +- +- if (block != Blocks.obsidian) +- { +- break label56; +- } ++ break label56; ++ // CraftBukkit start - add the block to our list + } +- else if (j == this.field_150868_h - 1) ++ else + { +- block = this.field_150867_a.getBlock(k + Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][1]], i, l + Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][1]]); +- +- if (block != Blocks.obsidian) +- { +- break label56; +- } ++ blocks.add(bworld.getBlockAt(k + Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][1]], i, l + Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][1]])); ++ // CraftBukkit end + } + } + } ++ } + +- for (i = 0; i < this.field_150868_h; ++i) +- { +- j = this.field_150861_f.posX + i * Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][1]]; +- k = this.field_150861_f.posY + this.field_150862_g; +- l = this.field_150861_f.posZ + i * Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][1]]; ++ for (i = 0; i < this.field_150868_h; ++i) ++ { ++ j = this.field_150861_f.posX + i * Direction.offsetX[BlockPortal.field_150001_a[this.field_150865_b][1]]; ++ k = this.field_150861_f.posY + this.field_150862_g; ++ l = this.field_150861_f.posZ + i * Direction.offsetZ[BlockPortal.field_150001_a[this.field_150865_b][1]]; + +- if (this.field_150867_a.getBlock(j, k, l) != Blocks.obsidian) +- { +- this.field_150862_g = 0; +- break; +- } +- } +- +- if (this.field_150862_g <= 21 && this.field_150862_g >= 3) ++ if (this.field_150867_a.getBlock(j, k, l) != Blocks.obsidian) + { +- return this.field_150862_g; +- } +- else +- { +- this.field_150861_f = null; +- this.field_150868_h = 0; + this.field_150862_g = 0; +- return 0; ++ break; + } + } + +- protected boolean func_150857_a(Block p_150857_1_) ++ if (this.field_150862_g <= 21 && this.field_150862_g >= 3) + { +- return p_150857_1_.blockMaterial == Material.air || p_150857_1_ == Blocks.fire || p_150857_1_ == Blocks.portal; ++ return this.field_150862_g; + } ++ else ++ { ++ this.field_150861_f = null; ++ this.field_150868_h = 0; ++ this.field_150862_g = 0; ++ return 0; ++ } ++ } + +- public boolean func_150860_b() ++ protected boolean func_150857_a(Block p_150857_1_) ++ { ++ return p_150857_1_.blockMaterial == Material.air || p_150857_1_ == Blocks.fire || p_150857_1_ == Blocks.portal; ++ } ++ ++ public boolean func_150860_b() ++ { ++ return this.field_150861_f != null && this.field_150868_h >= 2 && this.field_150868_h <= 21 && this.field_150862_g >= 3 && this.field_150862_g <= 21; ++ } ++ ++ // Cauldron start - vanilla compatibility ++ public void func_150859_c() ++ { ++ this.CB_func_150859_c(); ++ } ++ // Cauldron end ++ ++ public boolean CB_func_150859_c() ++ { ++ org.bukkit.World bworld = this.field_150867_a.getWorld(); ++ ++ // Copy below for loop ++ for (int i = 0; i < this.field_150868_h; ++i) + { +- return this.field_150861_f != null && this.field_150868_h >= 2 && this.field_150868_h <= 21 && this.field_150862_g >= 3 && this.field_150862_g <= 21; ++ int j = this.field_150861_f.posX + Direction.offsetX[this.field_150866_c] * i; ++ int k = this.field_150861_f.posZ + Direction.offsetZ[this.field_150866_c] * i; ++ ++ for (int l = 0; l < this.field_150862_g; ++l) ++ { ++ int i1 = this.field_150861_f.posY + l; ++ bworld.getBlockAt(j, i1, k); ++ } + } + +- public void func_150859_c() ++ PortalCreateEvent event = new PortalCreateEvent(blocks, bworld, PortalCreateEvent.CreateReason.FIRE); ++ this.field_150867_a.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) + { ++ return false; ++ } ++ // CraftBukkit end ++ + for (int i = 0; i < this.field_150868_h; ++i) + { + int j = this.field_150861_f.posX + Direction.offsetX[this.field_150866_c] * i; + int k = this.field_150861_f.posZ + Direction.offsetZ[this.field_150866_c] * i; +- ++ + for (int l = 0; l < this.field_150862_g; ++l) + { + int i1 = this.field_150861_f.posY + l; + this.field_150867_a.setBlock(j, i1, k, Blocks.portal, this.field_150865_b, 2); + } + } ++ ++ return true; // CraftBukkit + } + } + } diff --git a/patches/net/minecraft/block/BlockPressurePlate.java.patch b/patches/net/minecraft/block/BlockPressurePlate.java.patch new file mode 100644 index 0000000..fdae2f7 --- /dev/null +++ b/patches/net/minecraft/block/BlockPressurePlate.java.patch @@ -0,0 +1,47 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockPressurePlate.java ++++ ../src-work/minecraft/net/minecraft/block/BlockPressurePlate.java +@@ -8,6 +8,8 @@ + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit ++ + public class BlockPressurePlate extends BlockBasePressurePlate + { + private BlockPressurePlate.Sensitivity field_150069_a; +@@ -54,8 +56,34 @@ + + while (iterator.hasNext()) + { +- Entity entity = (Entity)iterator.next(); ++ Entity entity = (Entity) iterator.next(); + ++ // CraftBukkit start - Call interact event when turning on a pressure plate ++ if (this.func_150060_c(p_150065_1_.getBlockMetadata(p_150065_2_, p_150065_3_, p_150065_4_)) == 0) ++ { ++ org.bukkit.World bworld = p_150065_1_.getWorld(); ++ org.bukkit.plugin.PluginManager manager = p_150065_1_.getServer().getPluginManager(); ++ org.bukkit.event.Cancellable cancellable; ++ ++ if (entity instanceof EntityPlayer) ++ { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityPlayer) entity, org.bukkit.event.block.Action.PHYSICAL, p_150065_2_, p_150065_3_, p_150065_4_, -1, null); ++ } ++ else ++ { ++ cancellable = new EntityInteractEvent(entity.getBukkitEntity(), bworld.getBlockAt(p_150065_2_, p_150065_3_, p_150065_4_)); ++ manager.callEvent((EntityInteractEvent) cancellable); ++ } ++ ++ // We only want to block turning the plate on if all events are cancelled ++ if (cancellable.isCancelled()) ++ { ++ continue; ++ } ++ } ++ ++ // CraftBukkit end ++ + if (!entity.doesEntityNotTriggerPressurePlate()) + { + return 15; diff --git a/patches/net/minecraft/block/BlockPressurePlateWeighted.java.patch b/patches/net/minecraft/block/BlockPressurePlateWeighted.java.patch new file mode 100644 index 0000000..7f8c979 --- /dev/null +++ b/patches/net/minecraft/block/BlockPressurePlateWeighted.java.patch @@ -0,0 +1,63 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockPressurePlateWeighted.java ++++ ../src-work/minecraft/net/minecraft/block/BlockPressurePlateWeighted.java +@@ -5,6 +5,12 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++// CraftBukkit start ++import java.util.List; ++import net.minecraft.entity.player.EntityPlayer; ++import org.bukkit.event.entity.EntityInteractEvent; ++// CraftBukkit end ++ + public class BlockPressurePlateWeighted extends BlockBasePressurePlate + { + private final int field_150068_a; +@@ -18,18 +24,43 @@ + + protected int func_150065_e(World p_150065_1_, int p_150065_2_, int p_150065_3_, int p_150065_4_) + { +- int l = Math.min(p_150065_1_.getEntitiesWithinAABB(Entity.class, this.func_150061_a(p_150065_2_, p_150065_3_, p_150065_4_)).size(), this.field_150068_a); ++ // CraftBukkit start ++ int l = 0; ++ java.util.Iterator iterator = p_150065_1_.getEntitiesWithinAABB(Entity.class, this.func_150061_a(p_150065_2_, p_150065_3_, p_150065_4_)).iterator(); + +- if (l <= 0) ++ while (iterator.hasNext()) + { +- return 0; ++ Entity entity = (Entity) iterator.next(); ++ org.bukkit.event.Cancellable cancellable; ++ ++ if (entity instanceof EntityPlayer) ++ { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityPlayer) entity, org.bukkit.event.block.Action.PHYSICAL, p_150065_2_, p_150065_3_, p_150065_4_, -1, null); + } + else + { ++ cancellable = new EntityInteractEvent(entity.getBukkitEntity(), p_150065_1_.getWorld().getBlockAt(p_150065_2_, p_150065_3_, p_150065_4_)); ++ p_150065_1_.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); ++ } ++ ++ // We only want to block turning the plate on if all events are cancelled ++ if (!cancellable.isCancelled()) ++ { ++ l++; ++ } ++ } ++ ++ l = Math.min(l, this.field_150068_a); ++ // CraftBukkit end ++ ++ if (l <= 0) ++ { ++ return 0; ++ } ++ + float f = (float)Math.min(this.field_150068_a, l) / (float)this.field_150068_a; + return MathHelper.ceiling_float_int(f * 15.0F); + } +- } + + protected int func_150060_c(int p_150060_1_) + { diff --git a/patches/net/minecraft/block/BlockPumpkin.java.patch b/patches/net/minecraft/block/BlockPumpkin.java.patch new file mode 100644 index 0000000..7ec5dfe --- /dev/null +++ b/patches/net/minecraft/block/BlockPumpkin.java.patch @@ -0,0 +1,126 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockPumpkin.java ++++ ../src-work/minecraft/net/minecraft/block/BlockPumpkin.java +@@ -14,6 +14,12 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.util.BlockStateListPopulator; ++import org.bukkit.event.block.BlockRedstoneEvent; ++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; ++// CraftBukkit end ++ + public class BlockPumpkin extends BlockDirectional + { + private boolean field_149985_a; +@@ -45,15 +51,18 @@ + { + if (!p_149726_1_.isRemote) + { +- p_149726_1_.setBlock(p_149726_2_, p_149726_3_, p_149726_4_, getBlockById(0), 0, 2); +- p_149726_1_.setBlock(p_149726_2_, p_149726_3_ - 1, p_149726_4_, getBlockById(0), 0, 2); +- p_149726_1_.setBlock(p_149726_2_, p_149726_3_ - 2, p_149726_4_, getBlockById(0), 0, 2); ++ // CraftBukkit start - Use BlockStateListPopulator ++ BlockStateListPopulator blockList = new BlockStateListPopulator(p_149726_1_.getWorld()); ++ blockList.setTypeId(p_149726_2_, p_149726_3_, p_149726_4_, 0); ++ blockList.setTypeId(p_149726_2_, p_149726_3_ - 1, p_149726_4_, 0); ++ blockList.setTypeId(p_149726_2_, p_149726_3_ - 2, p_149726_4_, 0); + EntitySnowman entitysnowman = new EntitySnowman(p_149726_1_); +- entitysnowman.setLocationAndAngles((double)p_149726_2_ + 0.5D, (double)p_149726_3_ - 1.95D, (double)p_149726_4_ + 0.5D, 0.0F, 0.0F); +- p_149726_1_.spawnEntityInWorld(entitysnowman); +- p_149726_1_.notifyBlockChange(p_149726_2_, p_149726_3_, p_149726_4_, getBlockById(0)); +- p_149726_1_.notifyBlockChange(p_149726_2_, p_149726_3_ - 1, p_149726_4_, getBlockById(0)); +- p_149726_1_.notifyBlockChange(p_149726_2_, p_149726_3_ - 2, p_149726_4_, getBlockById(0)); ++ entitysnowman.setLocationAndAngles((double) p_149726_2_ + 0.5D, (double) p_149726_3_ - 1.95D, (double) p_149726_4_ + 0.5D, 0.0F, 0.0F); ++ ++ if (p_149726_1_.addEntity(entitysnowman, SpawnReason.BUILD_SNOWMAN)) ++ { ++ blockList.updateList(); ++ } + } + + for (int i1 = 0; i1 < 120; ++i1) +@@ -68,45 +77,38 @@ + + if (flag || flag1) + { +- p_149726_1_.setBlock(p_149726_2_, p_149726_3_, p_149726_4_, getBlockById(0), 0, 2); +- p_149726_1_.setBlock(p_149726_2_, p_149726_3_ - 1, p_149726_4_, getBlockById(0), 0, 2); +- p_149726_1_.setBlock(p_149726_2_, p_149726_3_ - 2, p_149726_4_, getBlockById(0), 0, 2); ++ // CraftBukkit start - Use BlockStateListPopulator ++ BlockStateListPopulator blockList = new BlockStateListPopulator(p_149726_1_.getWorld()); ++ blockList.setTypeId(p_149726_2_, p_149726_3_, p_149726_4_, 0); ++ blockList.setTypeId(p_149726_2_, p_149726_3_ - 1, p_149726_4_, 0); ++ blockList.setTypeId(p_149726_2_, p_149726_3_ - 2, p_149726_4_, 0); + + if (flag) + { +- p_149726_1_.setBlock(p_149726_2_ - 1, p_149726_3_ - 1, p_149726_4_, getBlockById(0), 0, 2); +- p_149726_1_.setBlock(p_149726_2_ + 1, p_149726_3_ - 1, p_149726_4_, getBlockById(0), 0, 2); ++ blockList.setTypeId(p_149726_2_ - 1, p_149726_3_ - 1, p_149726_4_, 0); ++ blockList.setTypeId(p_149726_2_ + 1, p_149726_3_ - 1, p_149726_4_, 0); + } + else + { +- p_149726_1_.setBlock(p_149726_2_, p_149726_3_ - 1, p_149726_4_ - 1, getBlockById(0), 0, 2); +- p_149726_1_.setBlock(p_149726_2_, p_149726_3_ - 1, p_149726_4_ + 1, getBlockById(0), 0, 2); ++ blockList.setTypeId(p_149726_2_, p_149726_3_ - 1, p_149726_4_ - 1, 0); ++ blockList.setTypeId(p_149726_2_, p_149726_3_ - 1, p_149726_4_ + 1, 0); + } + + EntityIronGolem entityirongolem = new EntityIronGolem(p_149726_1_); + entityirongolem.setPlayerCreated(true); +- entityirongolem.setLocationAndAngles((double)p_149726_2_ + 0.5D, (double)p_149726_3_ - 1.95D, (double)p_149726_4_ + 0.5D, 0.0F, 0.0F); +- p_149726_1_.spawnEntityInWorld(entityirongolem); ++ entityirongolem.setLocationAndAngles((double) p_149726_2_ + 0.5D, (double) p_149726_3_ - 1.95D, (double) p_149726_4_ + 0.5D, 0.0F, 0.0F); + +- for (int l = 0; l < 120; ++l) ++ if (p_149726_1_.addEntity(entityirongolem, SpawnReason.BUILD_IRONGOLEM)) + { +- p_149726_1_.spawnParticle("snowballpoof", (double)p_149726_2_ + p_149726_1_.rand.nextDouble(), (double)(p_149726_3_ - 2) + p_149726_1_.rand.nextDouble() * 3.9D, (double)p_149726_4_ + p_149726_1_.rand.nextDouble(), 0.0D, 0.0D, 0.0D); +- } ++ for (int i1 = 0; i1 < 120; ++i1) ++ { ++ p_149726_1_.spawnParticle("snowballpoof", (double) p_149726_2_ + p_149726_1_.rand.nextDouble(), (double)(p_149726_3_ - 2) + p_149726_1_.rand.nextDouble() * 3.9D, (double) p_149726_4_ + p_149726_1_.rand.nextDouble(), 0.0D, 0.0D, 0.0D); ++ } + +- p_149726_1_.notifyBlockChange(p_149726_2_, p_149726_3_, p_149726_4_, getBlockById(0)); +- p_149726_1_.notifyBlockChange(p_149726_2_, p_149726_3_ - 1, p_149726_4_, getBlockById(0)); +- p_149726_1_.notifyBlockChange(p_149726_2_, p_149726_3_ - 2, p_149726_4_, getBlockById(0)); +- +- if (flag) +- { +- p_149726_1_.notifyBlockChange(p_149726_2_ - 1, p_149726_3_ - 1, p_149726_4_, getBlockById(0)); +- p_149726_1_.notifyBlockChange(p_149726_2_ + 1, p_149726_3_ - 1, p_149726_4_, getBlockById(0)); ++ blockList.updateList(); + } +- else +- { +- p_149726_1_.notifyBlockChange(p_149726_2_, p_149726_3_ - 1, p_149726_4_ - 1, getBlockById(0)); +- p_149726_1_.notifyBlockChange(p_149726_2_, p_149726_3_ - 1, p_149726_4_ + 1, getBlockById(0)); +- } ++ ++ // CraftBukkit end + } + } + } +@@ -122,6 +124,19 @@ + p_149689_1_.setBlockMetadataWithNotify(p_149689_2_, p_149689_3_, p_149689_4_, l, 2); + } + ++ // CraftBukkit start ++ public void onNeighborBlockChange(World world, int i, int j, int k, Block block) ++ { ++ if (block != null && block.canProvidePower()) ++ { ++ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(i, j, k); ++ int power = bukkitBlock.getBlockPower(); ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, power, power); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ } ++ } ++ // CraftBukkit end ++ + @SideOnly(Side.CLIENT) + public void registerBlockIcons(IIconRegister p_149651_1_) + { diff --git a/patches/net/minecraft/block/BlockRailDetector.java.patch b/patches/net/minecraft/block/BlockRailDetector.java.patch new file mode 100644 index 0000000..0441e25 --- /dev/null +++ b/patches/net/minecraft/block/BlockRailDetector.java.patch @@ -0,0 +1,29 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockRailDetector.java ++++ ../src-work/minecraft/net/minecraft/block/BlockRailDetector.java +@@ -16,6 +16,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockRailDetector extends BlockRailBase + { + @SideOnly(Side.CLIENT) +@@ -86,6 +88,17 @@ + flag1 = true; + } + ++ // CraftBukkit start ++ if (flag != flag1) ++ { ++ org.bukkit.block.Block block = p_150054_1_.getWorld().getBlockAt(p_150054_2_, p_150054_3_, p_150054_4_); ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, flag ? 15 : 0, flag1 ? 15 : 0); ++ p_150054_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ flag1 = eventRedstone.getNewCurrent() > 0; ++ } ++ ++ // CraftBukkit end ++ + if (flag1 && !flag) + { + p_150054_1_.setBlockMetadataWithNotify(p_150054_2_, p_150054_3_, p_150054_4_, p_150054_5_ | 8, 3); diff --git a/patches/net/minecraft/block/BlockRedstoneDiode.java.patch b/patches/net/minecraft/block/BlockRedstoneDiode.java.patch new file mode 100644 index 0000000..5dabe9b --- /dev/null +++ b/patches/net/minecraft/block/BlockRedstoneDiode.java.patch @@ -0,0 +1,43 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockRedstoneDiode.java ++++ ../src-work/minecraft/net/minecraft/block/BlockRedstoneDiode.java +@@ -13,6 +13,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public abstract class BlockRedstoneDiode extends BlockDirectional + { + protected final boolean isRepeaterPowered; +@@ -44,16 +46,30 @@ + { + int l = p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_, p_149674_4_); + +- if (!this.func_149910_g(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, l)) ++ if (!this.func_149910_g((IBlockAccess) p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, l)) // CraftBukkit - Cast world to IBlockAccess to call the right method. + { + boolean flag = this.isGettingInput(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, l); + + if (this.isRepeaterPowered && !flag) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, 15, 0).getNewCurrent() != 0) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, this.getBlockUnpowered(), l, 2); + } + else if (!this.isRepeaterPowered) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, 0, 15).getNewCurrent() != 15) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, this.getBlockPowered(), l, 2); + + if (!flag) diff --git a/patches/net/minecraft/block/BlockRedstoneLight.java.patch b/patches/net/minecraft/block/BlockRedstoneLight.java.patch new file mode 100644 index 0000000..9103834 --- /dev/null +++ b/patches/net/minecraft/block/BlockRedstoneLight.java.patch @@ -0,0 +1,53 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockRedstoneLight.java ++++ ../src-work/minecraft/net/minecraft/block/BlockRedstoneLight.java +@@ -9,6 +9,8 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockRedstoneLight extends Block + { + private final boolean field_150171_a; +@@ -35,6 +37,13 @@ + } + else if (!this.field_150171_a && p_149726_1_.isBlockIndirectlyGettingPowered(p_149726_2_, p_149726_3_, p_149726_4_)) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(p_149726_1_, p_149726_2_, p_149726_3_, p_149726_4_, 0, 15).getNewCurrent() != 15) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + p_149726_1_.setBlock(p_149726_2_, p_149726_3_, p_149726_4_, Blocks.lit_redstone_lamp, 0, 2); + } + } +@@ -50,6 +59,13 @@ + } + else if (!this.field_150171_a && p_149695_1_.isBlockIndirectlyGettingPowered(p_149695_2_, p_149695_3_, p_149695_4_)) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(p_149695_1_, p_149695_2_, p_149695_3_, p_149695_4_, 0, 15).getNewCurrent() != 15) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + p_149695_1_.setBlock(p_149695_2_, p_149695_3_, p_149695_4_, Blocks.lit_redstone_lamp, 0, 2); + } + } +@@ -59,6 +75,13 @@ + { + if (!p_149674_1_.isRemote && this.field_150171_a && !p_149674_1_.isBlockIndirectlyGettingPowered(p_149674_2_, p_149674_3_, p_149674_4_)) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, 15, 0).getNewCurrent() != 0) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, Blocks.redstone_lamp, 0, 2); + } + } diff --git a/patches/net/minecraft/block/BlockRedstoneOre.java.patch b/patches/net/minecraft/block/BlockRedstoneOre.java.patch new file mode 100644 index 0000000..455b1d2 --- /dev/null +++ b/patches/net/minecraft/block/BlockRedstoneOre.java.patch @@ -0,0 +1,44 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockRedstoneOre.java ++++ ../src-work/minecraft/net/minecraft/block/BlockRedstoneOre.java +@@ -13,6 +13,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit ++ + public class BlockRedstoneOre extends Block + { + private boolean field_150187_a; +@@ -43,8 +45,30 @@ + + public void onEntityWalking(World p_149724_1_, int p_149724_2_, int p_149724_3_, int p_149724_4_, Entity p_149724_5_) + { +- this.func_150185_e(p_149724_1_, p_149724_2_, p_149724_3_, p_149724_4_); +- super.onEntityWalking(p_149724_1_, p_149724_2_, p_149724_3_, p_149724_4_, p_149724_5_); ++ // CraftBukkit start ++ if (p_149724_5_ instanceof EntityPlayer) ++ { ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityPlayer) p_149724_5_, org.bukkit.event.block.Action.PHYSICAL, p_149724_2_, p_149724_3_, p_149724_4_, -1, null); ++ ++ if (!event.isCancelled()) ++ { ++ this.func_150185_e(p_149724_1_, p_149724_2_, p_149724_3_, p_149724_4_); ++ super.onEntityWalking(p_149724_1_, p_149724_2_, p_149724_3_, p_149724_4_, p_149724_5_); ++ } ++ } ++ else ++ { ++ EntityInteractEvent event = new EntityInteractEvent(p_149724_5_.getBukkitEntity(), p_149724_1_.getWorld().getBlockAt(p_149724_2_, p_149724_3_, p_149724_4_)); ++ p_149724_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.func_150185_e(p_149724_1_, p_149724_2_, p_149724_3_, p_149724_4_); ++ super.onEntityWalking(p_149724_1_, p_149724_2_, p_149724_3_, p_149724_4_, p_149724_5_); ++ } ++ } ++ ++ // CraftBukkit end + } + + public boolean onBlockActivated(World p_149727_1_, int p_149727_2_, int p_149727_3_, int p_149727_4_, EntityPlayer p_149727_5_, int p_149727_6_, float p_149727_7_, float p_149727_8_, float p_149727_9_) diff --git a/patches/net/minecraft/block/BlockRedstoneTorch.java.patch b/patches/net/minecraft/block/BlockRedstoneTorch.java.patch new file mode 100644 index 0000000..483680f --- /dev/null +++ b/patches/net/minecraft/block/BlockRedstoneTorch.java.patch @@ -0,0 +1,62 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockRedstoneTorch.java ++++ ../src-work/minecraft/net/minecraft/block/BlockRedstoneTorch.java +@@ -13,6 +13,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockRedstoneTorch extends BlockTorch + { + private boolean field_150113_a; +@@ -125,10 +127,30 @@ + list.remove(0); + } + ++ // CraftBukkit start ++ org.bukkit.plugin.PluginManager manager = p_149674_1_.getServer().getPluginManager(); ++ org.bukkit.block.Block block = p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_); ++ int oldCurrent = this.field_150113_a ? 15 : 0; ++ BlockRedstoneEvent event = new BlockRedstoneEvent(block, oldCurrent, oldCurrent); ++ // CraftBukkit end ++ + if (this.field_150113_a) + { + if (flag) + { ++ // CraftBukkit start ++ if (oldCurrent != 0) ++ { ++ event.setNewCurrent(0); ++ manager.callEvent(event); ++ ++ if (event.getNewCurrent() != 0) ++ { ++ return; ++ } ++ } ++ ++ // CraftBukkit end + p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, Blocks.unlit_redstone_torch, p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_, p_149674_4_), 3); + + if (this.func_150111_a(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, true)) +@@ -147,6 +169,19 @@ + } + else if (!flag && !this.func_150111_a(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, false)) + { ++ // CraftBukkit start ++ if (oldCurrent != 15) ++ { ++ event.setNewCurrent(15); ++ manager.callEvent(event); ++ ++ if (event.getNewCurrent() != 15) ++ { ++ return; ++ } ++ } ++ ++ // CraftBukkit end + p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, Blocks.redstone_torch, p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_, p_149674_4_), 3); + } + } diff --git a/patches/net/minecraft/block/BlockRedstoneWire.java.patch b/patches/net/minecraft/block/BlockRedstoneWire.java.patch new file mode 100644 index 0000000..6387aae --- /dev/null +++ b/patches/net/minecraft/block/BlockRedstoneWire.java.patch @@ -0,0 +1,39 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockRedstoneWire.java ++++ ../src-work/minecraft/net/minecraft/block/BlockRedstoneWire.java +@@ -18,6 +18,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockRedstoneWire extends Block + { + private boolean field_150181_a = true; +@@ -159,8 +161,17 @@ + i3 = l1; + } + ++ // CraftBukkit start + if (k1 != i3) + { ++ BlockRedstoneEvent event = new BlockRedstoneEvent(p_150175_1_.getWorld().getBlockAt(p_150175_2_, p_150175_3_, p_150175_4_), k1, i3); ++ p_150175_1_.getServer().getPluginManager().callEvent(event); ++ i3 = event.getNewCurrent(); ++ } ++ ++ // CraftBukkit end ++ if (k1 != i3) ++ { + p_150175_1_.setBlockMetadataWithNotify(p_150175_2_, p_150175_3_, p_150175_4_, i3, 2); + this.field_150179_b.add(new ChunkPosition(p_150175_2_, p_150175_3_, p_150175_4_)); + this.field_150179_b.add(new ChunkPosition(p_150175_2_ - 1, p_150175_3_, p_150175_4_)); +@@ -294,7 +305,8 @@ + } + } + +- private int func_150178_a(World p_150178_1_, int p_150178_2_, int p_150178_3_, int p_150178_4_, int p_150178_5_) ++ // CraftBukkit - private -> public ++ public int func_150178_a(World p_150178_1_, int p_150178_2_, int p_150178_3_, int p_150178_4_, int p_150178_5_) + { + if (p_150178_1_.getBlock(p_150178_2_, p_150178_3_, p_150178_4_) != this) + { diff --git a/patches/net/minecraft/block/BlockReed.java.patch b/patches/net/minecraft/block/BlockReed.java.patch new file mode 100644 index 0000000..bb17ea1 --- /dev/null +++ b/patches/net/minecraft/block/BlockReed.java.patch @@ -0,0 +1,14 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockReed.java ++++ ../src-work/minecraft/net/minecraft/block/BlockReed.java +@@ -44,9 +44,9 @@ + { + int i1 = p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_, p_149674_4_); + +- if (i1 == 15) ++ if (i1 >= (byte) range(3, (p_149674_1_.growthOdds / p_149674_1_.getSpigotConfig().caneModifier * 15) + 0.5F, 15)) // Spigot // Cauldron + { +- p_149674_1_.setBlock(p_149674_2_, p_149674_3_ + 1, p_149674_4_, this); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(p_149674_1_, p_149674_2_, p_149674_3_ + 1, p_149674_4_, this, 0); // CraftBukkit + p_149674_1_.setBlockMetadataWithNotify(p_149674_2_, p_149674_3_, p_149674_4_, 0, 4); + } + else diff --git a/patches/net/minecraft/block/BlockSapling.java.patch b/patches/net/minecraft/block/BlockSapling.java.patch new file mode 100644 index 0000000..897cf75 --- /dev/null +++ b/patches/net/minecraft/block/BlockSapling.java.patch @@ -0,0 +1,122 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockSapling.java ++++ ../src-work/minecraft/net/minecraft/block/BlockSapling.java +@@ -22,10 +22,18 @@ + import net.minecraft.world.gen.feature.WorldGenTrees; + import net.minecraft.world.gen.feature.WorldGenerator; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.TreeType; ++import org.bukkit.block.BlockState; ++import org.bukkit.event.world.StructureGrowEvent; ++// CraftBukkit end ++ + public class BlockSapling extends BlockBush implements IGrowable + { + public static final String[] field_149882_a = new String[] {"oak", "spruce", "birch", "jungle", "acacia", "roofed_oak"}; + private static final IIcon[] field_149881_b = new IIcon[field_149882_a.length]; ++ public static TreeType treeType; // CraftBukkit + private static final String __OBFID = "CL_00000305"; + + protected BlockSapling() +@@ -41,9 +49,31 @@ + { + super.updateTick(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_5_); + +- if (p_149674_1_.getBlockLightValue(p_149674_2_, p_149674_3_ + 1, p_149674_4_) >= 9 && p_149674_5_.nextInt(7) == 0) ++ if (p_149674_1_.getBlockLightValue(p_149674_2_, p_149674_3_ + 1, p_149674_4_) >= 9 && (p_149674_5_.nextInt(Math.max(2, (int)((p_149674_1_.growthOdds / p_149674_1_.getSpigotConfig().saplingModifier * 7) + 0.5F))) == 0)) // Spigot // Cauldron + { ++ // Cauldron start ++ p_149674_1_.captureTreeGeneration = true; + this.func_149879_c(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_5_); ++ p_149674_1_.captureTreeGeneration = false; ++ if (p_149674_1_.capturedBlockStates.size() > 0) ++ { ++ TreeType treeType = BlockSapling.treeType; ++ BlockSapling.treeType = null; ++ Location location = new Location(p_149674_1_.getWorld(), p_149674_2_, p_149674_3_, p_149674_4_); ++ List blocks = (List) p_149674_1_.capturedBlockStates.clone(); ++ p_149674_1_.capturedBlockStates.clear(); ++ StructureGrowEvent event = null; ++ if (treeType != null) { ++ event = new StructureGrowEvent(location, treeType, false, null, blocks); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ } ++ if (event == null || !event.isCancelled()) { ++ for (BlockState blockstate : blocks) { ++ blockstate.update(true); ++ } ++ } ++ } ++ //Cauldron end + } + } + } +@@ -73,7 +103,20 @@ + { + if (!net.minecraftforge.event.terraingen.TerrainGen.saplingGrowTree(p_149878_1_, p_149878_5_, p_149878_2_, p_149878_3_, p_149878_4_)) return; + int l = p_149878_1_.getBlockMetadata(p_149878_2_, p_149878_3_, p_149878_4_) & 7; +- Object object = p_149878_5_.nextInt(10) == 0 ? new WorldGenBigTree(true) : new WorldGenTrees(true); ++ // CraftBukkit start ++ Object object = null; ++ if (p_149878_5_.nextInt(10) == 0) ++ { ++ treeType = TreeType.BIG_TREE; // CraftBukkit ++ object = new WorldGenBigTree(true); ++ } ++ else ++ { ++ treeType = TreeType.TREE; // CraftBukkit ++ object = new WorldGenTrees(true); ++ } ++ // CraftBukkit end ++ + int i1 = 0; + int j1 = 0; + boolean flag = false; +@@ -84,6 +127,7 @@ + default: + break; + case 1: ++ treeType = TreeType.REDWOOD; // CraftBukkit + label78: + + for (i1 = 0; i1 >= -1; --i1) +@@ -108,6 +152,7 @@ + + break; + case 2: ++ treeType = TreeType.BIRCH; // CraftBukkit + object = new WorldGenForest(true, false); + break; + case 3: +@@ -119,6 +164,7 @@ + { + if (this.func_149880_a(p_149878_1_, p_149878_2_ + i1, p_149878_3_, p_149878_4_ + j1, 3) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1 + 1, p_149878_3_, p_149878_4_ + j1, 3) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1, p_149878_3_, p_149878_4_ + j1 + 1, 3) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1 + 1, p_149878_3_, p_149878_4_ + j1 + 1, 3)) + { ++ treeType = TreeType.JUNGLE; // CraftBukkit + object = new WorldGenMegaJungle(true, 10, 20, 3, 3); + flag = true; + break label93; +@@ -130,11 +176,13 @@ + { + j1 = 0; + i1 = 0; ++ treeType = TreeType.SMALL_JUNGLE; // CraftBukkit + object = new WorldGenTrees(true, 4 + p_149878_5_.nextInt(7), 3, 3, false); + } + + break; + case 4: ++ treeType = TreeType.ACACIA; // CraftBukkit + object = new WorldGenSavannaTree(true); + break; + case 5: +@@ -147,6 +195,7 @@ + if (this.func_149880_a(p_149878_1_, p_149878_2_ + i1, p_149878_3_, p_149878_4_ + j1, 5) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1 + 1, p_149878_3_, p_149878_4_ + j1, 5) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1, p_149878_3_, p_149878_4_ + j1 + 1, 5) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1 + 1, p_149878_3_, p_149878_4_ + j1 + 1, 5)) + { + object = new WorldGenCanopyTree(true); ++ treeType = TreeType.DARK_OAK; // CraftBukkit + flag = true; + break label108; + } diff --git a/patches/net/minecraft/block/BlockSign.java.patch b/patches/net/minecraft/block/BlockSign.java.patch new file mode 100644 index 0000000..5e0093b --- /dev/null +++ b/patches/net/minecraft/block/BlockSign.java.patch @@ -0,0 +1,29 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockSign.java ++++ ../src-work/minecraft/net/minecraft/block/BlockSign.java +@@ -14,6 +14,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockSign extends BlockContainer + { + private Class field_149968_a; +@@ -163,6 +165,17 @@ + } + + super.onNeighborBlockChange(p_149695_1_, p_149695_2_, p_149695_3_, p_149695_4_, p_149695_5_); ++ ++ // CraftBukkit start ++ if (p_149695_5_ != null && p_149695_5_.canProvidePower()) ++ { ++ org.bukkit.block.Block bukkitBlock = p_149695_1_.getWorld().getBlockAt(p_149695_2_, p_149695_3_, p_149695_4_); ++ int power = bukkitBlock.getBlockPower(); ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, power, power); ++ p_149695_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ } ++ ++ // CraftBukkit end + } + + @SideOnly(Side.CLIENT) diff --git a/patches/net/minecraft/block/BlockSkull.java.patch b/patches/net/minecraft/block/BlockSkull.java.patch new file mode 100644 index 0000000..a11ef00 --- /dev/null +++ b/patches/net/minecraft/block/BlockSkull.java.patch @@ -0,0 +1,156 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockSkull.java ++++ ../src-work/minecraft/net/minecraft/block/BlockSkull.java +@@ -27,6 +27,11 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.util.BlockStateListPopulator; ++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; ++// CraftBukkit end ++ + public class BlockSkull extends BlockContainer + { + private static final String __OBFID = "CL_00000307"; +@@ -172,16 +177,18 @@ + { + if (p_149965_1_.getBlock(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l) == Blocks.soul_sand && p_149965_1_.getBlock(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l + 1) == Blocks.soul_sand && p_149965_1_.getBlock(p_149965_2_, p_149965_3_ - 2, p_149965_4_ + l + 1) == Blocks.soul_sand && p_149965_1_.getBlock(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l + 2) == Blocks.soul_sand && this.func_149966_a(p_149965_1_, p_149965_2_, p_149965_3_, p_149965_4_ + l, 1) && this.func_149966_a(p_149965_1_, p_149965_2_, p_149965_3_, p_149965_4_ + l + 1, 1) && this.func_149966_a(p_149965_1_, p_149965_2_, p_149965_3_, p_149965_4_ + l + 2, 1)) + { ++ // CraftBukkit start - Use BlockStateListPopulator ++ BlockStateListPopulator blockList = new BlockStateListPopulator(p_149965_1_.getWorld()); + p_149965_1_.setBlockMetadataWithNotify(p_149965_2_, p_149965_3_, p_149965_4_ + l, 8, 2); + p_149965_1_.setBlockMetadataWithNotify(p_149965_2_, p_149965_3_, p_149965_4_ + l + 1, 8, 2); + p_149965_1_.setBlockMetadataWithNotify(p_149965_2_, p_149965_3_, p_149965_4_ + l + 2, 8, 2); +- p_149965_1_.setBlock(p_149965_2_, p_149965_3_, p_149965_4_ + l, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_, p_149965_3_, p_149965_4_ + l + 1, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_, p_149965_3_, p_149965_4_ + l + 2, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l + 1, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l + 2, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_, p_149965_3_ - 2, p_149965_4_ + l + 1, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_, p_149965_3_, p_149965_4_ + l, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_, p_149965_3_, p_149965_4_ + l + 1, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_, p_149965_3_, p_149965_4_ + l + 2, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l + 1, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l + 2, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_, p_149965_3_ - 2, p_149965_4_ + l + 1, getBlockById(0), 0, 2); + + if (!p_149965_1_.isRemote) + { +@@ -190,18 +197,21 @@ + entitywither.renderYawOffset = 90.0F; + entitywither.func_82206_m(); + +- if (!p_149965_1_.isRemote) ++ if (p_149965_1_.addEntity(entitywither, SpawnReason.BUILD_WITHER)) + { +- iterator = p_149965_1_.getEntitiesWithinAABB(EntityPlayer.class, entitywither.boundingBox.expand(50.0D, 50.0D, 50.0D)).iterator(); +- +- while (iterator.hasNext()) ++ if (!p_149965_1_.isRemote) + { +- entityplayer = (EntityPlayer)iterator.next(); +- entityplayer.triggerAchievement(AchievementList.field_150963_I); ++ iterator = p_149965_1_.getEntitiesWithinAABB(EntityPlayer.class, entitywither.boundingBox.expand(50.0D, 50.0D, 50.0D)).iterator(); ++ ++ while (iterator.hasNext()) ++ { ++ entityplayer = (EntityPlayer) iterator.next(); ++ entityplayer.triggerAchievement(AchievementList.field_150963_I); ++ } + } +- } + +- p_149965_1_.spawnEntityInWorld(entitywither); ++ blockList.updateList(); ++ } + } + + for (i1 = 0; i1 < 120; ++i1) +@@ -209,13 +219,7 @@ + p_149965_1_.spawnParticle("snowballpoof", (double)p_149965_2_ + p_149965_1_.rand.nextDouble(), (double)(p_149965_3_ - 2) + p_149965_1_.rand.nextDouble() * 3.9D, (double)(p_149965_4_ + l + 1) + p_149965_1_.rand.nextDouble(), 0.0D, 0.0D, 0.0D); + } + +- p_149965_1_.notifyBlockChange(p_149965_2_, p_149965_3_, p_149965_4_ + l, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_, p_149965_3_, p_149965_4_ + l + 1, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_, p_149965_3_, p_149965_4_ + l + 2, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l + 1, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_, p_149965_3_ - 1, p_149965_4_ + l + 2, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_, p_149965_3_ - 2, p_149965_4_ + l + 1, getBlockById(0)); ++ // CraftBukkit end + return; + } + } +@@ -224,16 +228,18 @@ + { + if (p_149965_1_.getBlock(p_149965_2_ + l, p_149965_3_ - 1, p_149965_4_) == Blocks.soul_sand && p_149965_1_.getBlock(p_149965_2_ + l + 1, p_149965_3_ - 1, p_149965_4_) == Blocks.soul_sand && p_149965_1_.getBlock(p_149965_2_ + l + 1, p_149965_3_ - 2, p_149965_4_) == Blocks.soul_sand && p_149965_1_.getBlock(p_149965_2_ + l + 2, p_149965_3_ - 1, p_149965_4_) == Blocks.soul_sand && this.func_149966_a(p_149965_1_, p_149965_2_ + l, p_149965_3_, p_149965_4_, 1) && this.func_149966_a(p_149965_1_, p_149965_2_ + l + 1, p_149965_3_, p_149965_4_, 1) && this.func_149966_a(p_149965_1_, p_149965_2_ + l + 2, p_149965_3_, p_149965_4_, 1)) + { ++ // CraftBukkit start - Use BlockStateListPopulator ++ BlockStateListPopulator blockList = new BlockStateListPopulator(p_149965_1_.getWorld()); + p_149965_1_.setBlockMetadataWithNotify(p_149965_2_ + l, p_149965_3_, p_149965_4_, 8, 2); + p_149965_1_.setBlockMetadataWithNotify(p_149965_2_ + l + 1, p_149965_3_, p_149965_4_, 8, 2); + p_149965_1_.setBlockMetadataWithNotify(p_149965_2_ + l + 2, p_149965_3_, p_149965_4_, 8, 2); +- p_149965_1_.setBlock(p_149965_2_ + l, p_149965_3_, p_149965_4_, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_ + l + 1, p_149965_3_, p_149965_4_, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_ + l + 2, p_149965_3_, p_149965_4_, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_ + l, p_149965_3_ - 1, p_149965_4_, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_ + l + 1, p_149965_3_ - 1, p_149965_4_, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_ + l + 2, p_149965_3_ - 1, p_149965_4_, getBlockById(0), 0, 2); +- p_149965_1_.setBlock(p_149965_2_ + l + 1, p_149965_3_ - 2, p_149965_4_, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_ + l, p_149965_3_, p_149965_4_, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_ + l + 1, p_149965_3_, p_149965_4_, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_ + l + 2, p_149965_3_, p_149965_4_, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_ + l, p_149965_3_ - 1, p_149965_4_, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_ + l + 1, p_149965_3_ - 1, p_149965_4_, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_ + l + 2, p_149965_3_ - 1, p_149965_4_, getBlockById(0), 0, 2); ++ blockList.setTypeAndData(p_149965_2_ + l + 1, p_149965_3_ - 2, p_149965_4_, getBlockById(0), 0, 2); + + if (!p_149965_1_.isRemote) + { +@@ -241,18 +247,21 @@ + entitywither.setLocationAndAngles((double)(p_149965_2_ + l) + 1.5D, (double)p_149965_3_ - 1.45D, (double)p_149965_4_ + 0.5D, 0.0F, 0.0F); + entitywither.func_82206_m(); + +- if (!p_149965_1_.isRemote) ++ if (p_149965_1_.addEntity(entitywither, SpawnReason.BUILD_WITHER)) + { +- iterator = p_149965_1_.getEntitiesWithinAABB(EntityPlayer.class, entitywither.boundingBox.expand(50.0D, 50.0D, 50.0D)).iterator(); +- +- while (iterator.hasNext()) ++ if (!p_149965_1_.isRemote) + { +- entityplayer = (EntityPlayer)iterator.next(); +- entityplayer.triggerAchievement(AchievementList.field_150963_I); ++ iterator = p_149965_1_.getEntitiesWithinAABB(EntityPlayer.class, entitywither.boundingBox.expand(50.0D, 50.0D, 50.0D)).iterator(); ++ ++ while (iterator.hasNext()) ++ { ++ entityplayer = (EntityPlayer) iterator.next(); ++ entityplayer.triggerAchievement(AchievementList.field_150963_I); ++ } + } +- } + +- p_149965_1_.spawnEntityInWorld(entitywither); ++ blockList.updateList(); ++ } + } + + for (i1 = 0; i1 < 120; ++i1) +@@ -260,13 +269,7 @@ + p_149965_1_.spawnParticle("snowballpoof", (double)(p_149965_2_ + l + 1) + p_149965_1_.rand.nextDouble(), (double)(p_149965_3_ - 2) + p_149965_1_.rand.nextDouble() * 3.9D, (double)p_149965_4_ + p_149965_1_.rand.nextDouble(), 0.0D, 0.0D, 0.0D); + } + +- p_149965_1_.notifyBlockChange(p_149965_2_ + l, p_149965_3_, p_149965_4_, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_ + l + 1, p_149965_3_, p_149965_4_, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_ + l + 2, p_149965_3_, p_149965_4_, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_ + l, p_149965_3_ - 1, p_149965_4_, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_ + l + 1, p_149965_3_ - 1, p_149965_4_, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_ + l + 2, p_149965_3_ - 1, p_149965_4_, getBlockById(0)); +- p_149965_1_.notifyBlockChange(p_149965_2_ + l + 1, p_149965_3_ - 2, p_149965_4_, getBlockById(0)); ++ // CraftBukkit end + return; + } + } diff --git a/patches/net/minecraft/block/BlockSnow.java.patch b/patches/net/minecraft/block/BlockSnow.java.patch new file mode 100644 index 0000000..86c0ed8 --- /dev/null +++ b/patches/net/minecraft/block/BlockSnow.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockSnow.java ++++ ../src-work/minecraft/net/minecraft/block/BlockSnow.java +@@ -114,6 +114,14 @@ + { + if (p_149674_1_.getSavedLightValue(EnumSkyBlock.Block, p_149674_2_, p_149674_3_, p_149674_4_) > 11) + { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_), Blocks.air).isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end ++ this.dropBlockAsItem(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_, p_149674_4_), 0); + p_149674_1_.setBlockToAir(p_149674_2_, p_149674_3_, p_149674_4_); + } + } diff --git a/patches/net/minecraft/block/BlockStaticLiquid.java.patch b/patches/net/minecraft/block/BlockStaticLiquid.java.patch new file mode 100644 index 0000000..8d27641 --- /dev/null +++ b/patches/net/minecraft/block/BlockStaticLiquid.java.patch @@ -0,0 +1,57 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockStaticLiquid.java ++++ ../src-work/minecraft/net/minecraft/block/BlockStaticLiquid.java +@@ -5,6 +5,8 @@ + import net.minecraft.init.Blocks; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockStaticLiquid extends BlockLiquid + { + private static final String __OBFID = "CL_00000315"; +@@ -43,6 +45,11 @@ + { + int l = p_149674_5_.nextInt(3); + int i1; ++ // CraftBukkit start - Prevent lava putting something on fire, remember igniter block coords ++ int x = p_149674_2_; ++ int y = p_149674_3_; ++ int z = p_149674_4_; ++ // CraftBukkit end + + for (i1 = 0; i1 < l; ++i1) + { +@@ -55,6 +62,16 @@ + { + if (this.isFlammable(p_149674_1_, p_149674_2_ - 1, p_149674_3_, p_149674_4_) || this.isFlammable(p_149674_1_, p_149674_2_ + 1, p_149674_3_, p_149674_4_) || this.isFlammable(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_ - 1) || this.isFlammable(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_ + 1) || this.isFlammable(p_149674_1_, p_149674_2_, p_149674_3_ - 1, p_149674_4_) || this.isFlammable(p_149674_1_, p_149674_2_, p_149674_3_ + 1, p_149674_4_)) + { ++ // CraftBukkit start - Prevent lava putting something on fire ++ if (p_149674_1_.getBlock(p_149674_2_, p_149674_3_, p_149674_4_) != Blocks.fire) ++ { ++ if (CraftEventFactory.callBlockIgniteEvent(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, x, y, z).isCancelled()) ++ { ++ continue; ++ } ++ } ++ ++ // CraftBukkit end + p_149674_1_.setBlock(p_149674_2_, p_149674_3_, p_149674_4_, Blocks.fire); + return; + } +@@ -77,6 +94,16 @@ + + if (p_149674_1_.isAirBlock(p_149674_2_, p_149674_3_ + 1, p_149674_4_) && this.isFlammable(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_)) + { ++ // CraftBukkit start - Prevent lava putting something on fire ++ if (p_149674_1_.getBlock(p_149674_2_, p_149674_3_ + 1, p_149674_4_) != Blocks.fire) ++ { ++ if (CraftEventFactory.callBlockIgniteEvent(p_149674_1_, p_149674_2_, p_149674_3_ + 1, p_149674_4_, x, y, z).isCancelled()) ++ { ++ continue; ++ } ++ } ++ ++ // CraftBukkit end + p_149674_1_.setBlock(p_149674_2_, p_149674_3_ + 1, p_149674_4_, Blocks.fire); + } + } diff --git a/patches/net/minecraft/block/BlockStem.java.patch b/patches/net/minecraft/block/BlockStem.java.patch new file mode 100644 index 0000000..41aed08 --- /dev/null +++ b/patches/net/minecraft/block/BlockStem.java.patch @@ -0,0 +1,37 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockStem.java ++++ ../src-work/minecraft/net/minecraft/block/BlockStem.java +@@ -17,6 +17,8 @@ + import net.minecraft.world.World; + import static net.minecraftforge.common.util.ForgeDirection.*; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockStem extends BlockBush implements IGrowable + { + private final Block field_149877_a; +@@ -46,14 +48,14 @@ + { + float f = this.func_149875_n(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_); + +- if (p_149674_5_.nextInt((int)(25.0F / f) + 1) == 0) ++ if (p_149674_5_.nextInt((int)(p_149674_1_.growthOdds / (this == Blocks.pumpkin_stem ? p_149674_1_.getSpigotConfig().pumpkinModifier : p_149674_1_.spigotConfig.melonModifier) * (25.0F / f)) + 1) == 0) // Spigot // Cauldron + { + int l = p_149674_1_.getBlockMetadata(p_149674_2_, p_149674_3_, p_149674_4_); + + if (l < 7) + { + ++l; +- p_149674_1_.setBlockMetadataWithNotify(p_149674_2_, p_149674_3_, p_149674_4_, l, 2); ++ CraftEventFactory.handleBlockGrowEvent(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, this, l); // CraftBukkit + } + else + { +@@ -105,7 +107,7 @@ + + if (p_149674_1_.isAirBlock(j1, p_149674_3_, k1) && (block.canSustainPlant(p_149674_1_, j1, p_149674_3_ - 1, k1, UP, this) || block == Blocks.dirt || block == Blocks.grass)) + { +- p_149674_1_.setBlock(j1, p_149674_3_, k1, this.field_149877_a); ++ CraftEventFactory.handleBlockGrowEvent(p_149674_1_, j1, p_149674_3_, k1, this.field_149877_a, 0); // CraftBukkit + } + } + } diff --git a/patches/net/minecraft/block/BlockTrapDoor.java.patch b/patches/net/minecraft/block/BlockTrapDoor.java.patch new file mode 100644 index 0000000..d2418ce --- /dev/null +++ b/patches/net/minecraft/block/BlockTrapDoor.java.patch @@ -0,0 +1,32 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockTrapDoor.java ++++ ../src-work/minecraft/net/minecraft/block/BlockTrapDoor.java +@@ -13,6 +13,8 @@ + import net.minecraft.world.World; + import net.minecraftforge.common.util.ForgeDirection; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockTrapDoor extends Block + { + /** Set this to allow trapdoors to remain free-floating */ +@@ -176,6 +178,20 @@ + + if (flag || p_149695_5_.canProvidePower()) + { ++ // CraftBukkit start ++ org.bukkit.World bworld = p_149695_1_.getWorld(); ++ org.bukkit.block.Block bblock = bworld.getBlockAt(p_149695_2_, p_149695_3_, p_149695_4_); ++ int power = bblock.getBlockPower(); ++ int oldPower = (p_149695_1_.getBlockMetadata(p_149695_2_, p_149695_3_, p_149695_4_) & 4) > 0 ? 15 : 0; ++ ++ if (oldPower == 0 ^ power == 0 || p_149695_5_.canProvidePower()) ++ { ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bblock, oldPower, power); ++ p_149695_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ flag = eventRedstone.getNewCurrent() > 0; ++ } ++ ++ // CraftBukkit end + this.func_150120_a(p_149695_1_, p_149695_2_, p_149695_3_, p_149695_4_, flag); + } + } diff --git a/patches/net/minecraft/block/BlockTripWire.java.patch b/patches/net/minecraft/block/BlockTripWire.java.patch new file mode 100644 index 0000000..d3721ed --- /dev/null +++ b/patches/net/minecraft/block/BlockTripWire.java.patch @@ -0,0 +1,63 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockTripWire.java ++++ ../src-work/minecraft/net/minecraft/block/BlockTripWire.java +@@ -16,6 +16,8 @@ + import net.minecraft.world.IBlockAccess; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit ++ + public class BlockTripWire extends Block + { + private static final String __OBFID = "CL_00000328"; +@@ -208,6 +210,51 @@ + } + } + ++ // CraftBukkit start - Call interact even when triggering connected tripwire ++ if (flag != flag1 && flag1 && (p_150140_1_.getBlockMetadata(p_150140_2_, p_150140_3_, p_150140_4_) & 4) == 4) ++ { ++ org.bukkit.World bworld = p_150140_1_.getWorld(); ++ org.bukkit.plugin.PluginManager manager = p_150140_1_.getServer().getPluginManager(); ++ org.bukkit.block.Block block = bworld.getBlockAt(p_150140_2_, p_150140_3_, p_150140_4_); ++ boolean allowed = false; ++ ++ // If all of the events are cancelled block the tripwire trigger, else allow ++ for (Object object : list) ++ { ++ if (object != null) ++ { ++ org.bukkit.event.Cancellable cancellable; ++ ++ if (object instanceof EntityPlayer) ++ { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityPlayer) object, org.bukkit.event.block.Action.PHYSICAL, p_150140_2_, p_150140_3_, p_150140_4_, -1, null); ++ } ++ else if (object instanceof Entity) ++ { ++ cancellable = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block); ++ manager.callEvent((EntityInteractEvent) cancellable); ++ } ++ else ++ { ++ continue; ++ } ++ ++ if (!cancellable.isCancelled()) ++ { ++ allowed = true; ++ break; ++ } ++ } ++ } ++ ++ if (!allowed) ++ { ++ return; ++ } ++ } ++ ++ // CraftBukkit end ++ + if (flag1 && !flag) + { + l |= 1; diff --git a/patches/net/minecraft/block/BlockTripWireHook.java.patch b/patches/net/minecraft/block/BlockTripWireHook.java.patch new file mode 100644 index 0000000..f860cea --- /dev/null +++ b/patches/net/minecraft/block/BlockTripWireHook.java.patch @@ -0,0 +1,29 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockTripWireHook.java ++++ ../src-work/minecraft/net/minecraft/block/BlockTripWireHook.java +@@ -12,6 +12,8 @@ + import net.minecraftforge.common.util.ForgeDirection; + import static net.minecraftforge.common.util.ForgeDirection.*; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockTripWireHook extends Block + { + private static final String __OBFID = "CL_00000329"; +@@ -210,6 +212,17 @@ + this.func_150135_a(p_150136_1_, l2, p_150136_3_, i3, flag4, flag5, flag2, flag3); + } + ++ // CraftBukkit start ++ org.bukkit.block.Block block = p_150136_1_.getWorld().getBlockAt(p_150136_2_, p_150136_3_, p_150136_4_); ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); ++ p_150136_1_.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if (eventRedstone.getNewCurrent() > 0) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.func_150135_a(p_150136_1_, p_150136_2_, p_150136_3_, p_150136_4_, flag4, flag5, flag2, flag3); + + if (!p_150136_5_) diff --git a/patches/net/minecraft/block/BlockVine.java.patch b/patches/net/minecraft/block/BlockVine.java.patch new file mode 100644 index 0000000..0eac4fe --- /dev/null +++ b/patches/net/minecraft/block/BlockVine.java.patch @@ -0,0 +1,77 @@ +--- ../src-base/minecraft/net/minecraft/block/BlockVine.java ++++ ../src-work/minecraft/net/minecraft/block/BlockVine.java +@@ -21,6 +21,8 @@ + import net.minecraftforge.common.ForgeHooks; + import net.minecraftforge.common.IShearable; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockVine extends Block implements IShearable + { + private static final String __OBFID = "CL_00000330"; +@@ -268,7 +270,11 @@ + + if (j2 > 0) + { +- p_149674_1_.setBlock(p_149674_2_, p_149674_3_ + 1, p_149674_4_, this, j2, 2); ++ // CraftBukkit start - Call BlockSpreadEvent ++ org.bukkit.block.Block source = p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_); ++ org.bukkit.block.Block block = p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_ + 1, p_149674_4_); ++ CraftEventFactory.handleBlockSpreadEvent(block, source, this, l1); ++ // CraftBukkit end + } + } + } +@@ -290,27 +296,34 @@ + { + l1 = k1 + 1 & 3; + i2 = k1 + 3 & 3; ++ // CraftBukkit start - Call BlockSpreadEvent ++ org.bukkit.block.Block source = p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_); ++ org.bukkit.block.Block bukkitBlock = p_149674_1_.getWorld().getBlockAt(p_149674_2_ + Direction.offsetX[k1], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1]); + + if ((i1 & 1 << l1) != 0 && this.func_150093_a(p_149674_1_.getBlock(p_149674_2_ + Direction.offsetX[k1] + Direction.offsetX[l1], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1] + Direction.offsetZ[l1]))) + { +- p_149674_1_.setBlock(p_149674_2_ + Direction.offsetX[k1], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1], this, 1 << l1, 2); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 1 << l1); + } + else if ((i1 & 1 << i2) != 0 && this.func_150093_a(p_149674_1_.getBlock(p_149674_2_ + Direction.offsetX[k1] + Direction.offsetX[i2], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1] + Direction.offsetZ[i2]))) + { +- p_149674_1_.setBlock(p_149674_2_ + Direction.offsetX[k1], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1], this, 1 << i2, 2); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 1 << i2); + } + else if ((i1 & 1 << l1) != 0 && p_149674_1_.isAirBlock(p_149674_2_ + Direction.offsetX[k1] + Direction.offsetX[l1], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1] + Direction.offsetZ[l1]) && this.func_150093_a(p_149674_1_.getBlock(p_149674_2_ + Direction.offsetX[l1], p_149674_3_, p_149674_4_ + Direction.offsetZ[l1]))) + { +- p_149674_1_.setBlock(p_149674_2_ + Direction.offsetX[k1] + Direction.offsetX[l1], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1] + Direction.offsetZ[l1], this, 1 << (k1 + 2 & 3), 2); ++ bukkitBlock = p_149674_1_.getWorld().getBlockAt(p_149674_2_ + Direction.offsetX[k1] + Direction.offsetX[l1], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1] + Direction.offsetZ[l1]); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 1 << (k1 + 2 & 3)); + } + else if ((i1 & 1 << i2) != 0 && p_149674_1_.isAirBlock(p_149674_2_ + Direction.offsetX[k1] + Direction.offsetX[i2], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1] + Direction.offsetZ[i2]) && this.func_150093_a(p_149674_1_.getBlock(p_149674_2_ + Direction.offsetX[i2], p_149674_3_, p_149674_4_ + Direction.offsetZ[i2]))) + { +- p_149674_1_.setBlock(p_149674_2_ + Direction.offsetX[k1] + Direction.offsetX[i2], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1] + Direction.offsetZ[i2], this, 1 << (k1 + 2 & 3), 2); ++ bukkitBlock = p_149674_1_.getWorld().getBlockAt(p_149674_2_ + Direction.offsetX[k1] + Direction.offsetX[i2], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1] + Direction.offsetZ[i2]); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 1 << (k1 + 2 & 3)); + } + else if (this.func_150093_a(p_149674_1_.getBlock(p_149674_2_ + Direction.offsetX[k1], p_149674_3_ + 1, p_149674_4_ + Direction.offsetZ[k1]))) + { +- p_149674_1_.setBlock(p_149674_2_ + Direction.offsetX[k1], p_149674_3_, p_149674_4_ + Direction.offsetZ[k1], this, 0, 2); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, 0); + } ++ ++ // CraftBukkit end + } + else if (block.blockMaterial.isOpaque() && block.renderAsNormalBlock()) + { +@@ -327,7 +340,11 @@ + + if (l1 > 0) + { +- p_149674_1_.setBlock(p_149674_2_, p_149674_3_ - 1, p_149674_4_, this, l1, 2); ++ // CraftBukkit start - Call BlockSpreadEvent ++ org.bukkit.block.Block source = p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_, p_149674_4_); ++ org.bukkit.block.Block bukkitBlock = p_149674_1_.getWorld().getBlockAt(p_149674_2_, p_149674_3_ - 1, p_149674_4_); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, l1); ++ // CraftBukkit end + } + } + else if (block == this) diff --git a/patches/net/minecraft/client/Minecraft.java.patch b/patches/net/minecraft/client/Minecraft.java.patch new file mode 100644 index 0000000..9134545 --- /dev/null +++ b/patches/net/minecraft/client/Minecraft.java.patch @@ -0,0 +1,26 @@ +--- ../src-base/minecraft/net/minecraft/client/Minecraft.java ++++ ../src-work/minecraft/net/minecraft/client/Minecraft.java +@@ -148,6 +148,7 @@ + import net.minecraft.util.Timer; + import net.minecraft.util.Util; + import net.minecraft.world.EnumDifficulty; ++import net.minecraft.world.MinecraftException; + import net.minecraft.world.WorldProviderEnd; + import net.minecraft.world.WorldProviderHell; + import net.minecraft.world.WorldSettings; +@@ -2644,7 +2645,14 @@ + + if (integratedserver != null) + { +- integratedserver.stopServer(); ++ try ++ { ++ integratedserver.stopServer(); ++ } ++ catch (MinecraftException e) ++ { ++ e.printStackTrace(); ++ } + } + } + } diff --git a/patches/net/minecraft/command/CommandHandler.java.patch b/patches/net/minecraft/command/CommandHandler.java.patch new file mode 100644 index 0000000..a5f91b0 --- /dev/null +++ b/patches/net/minecraft/command/CommandHandler.java.patch @@ -0,0 +1,74 @@ +--- ../src-base/minecraft/net/minecraft/command/CommandHandler.java ++++ ../src-work/minecraft/net/minecraft/command/CommandHandler.java +@@ -16,6 +16,11 @@ + + import net.minecraftforge.common.MinecraftForge; + import net.minecraftforge.event.CommandEvent; ++// Cauldron start ++import org.bukkit.craftbukkit.command.CraftSimpleCommandMap; ++import org.bukkit.craftbukkit.command.ModCustomCommand; ++import cpw.mods.fml.common.FMLCommonHandler; ++// Cauldron end + + public class CommandHandler implements ICommandManager + { +@@ -48,7 +53,7 @@ + throw new CommandNotFoundException(); + } + +- if (icommand.canCommandSenderUseCommand(p_71556_1_)) ++ if (true || icommand.canCommandSenderUseCommand(p_71556_1_)) // Cauldron start - disable check for permissions since we handle it on Bukkit side + { + CommandEvent event = new CommandEvent(icommand, p_71556_1_, astring); + if (MinecraftForge.EVENT_BUS.post(event)) +@@ -134,11 +139,30 @@ + + public ICommand registerCommand(ICommand p_71560_1_) + { +- List list = p_71560_1_.getCommandAliases(); +- this.commandMap.put(p_71560_1_.getCommandName(), p_71560_1_); +- this.commandSet.add(p_71560_1_); ++ // Cauldron start - register commands with permission nodes, defaulting to class name ++ return registerCommand(p_71560_1_, p_71560_1_.getClass().getName()); ++ } + ++ public ICommand registerCommand(String permissionGroup, ICommand par1ICommand) ++ { ++ return registerCommand(par1ICommand, permissionGroup + "." + par1ICommand.getCommandName()); ++ } ++ ++ public ICommand registerCommand(ICommand par1ICommand, String permissionNode) ++ { ++ List list = par1ICommand.getCommandAliases(); ++ this.commandMap.put(par1ICommand.getCommandName(), par1ICommand); ++ this.commandSet.add(par1ICommand); ++ // register vanilla commands with Bukkit to support permissions. ++ CraftSimpleCommandMap commandMap = FMLCommonHandler.instance().getMinecraftServerInstance().server.getCraftCommandMap(); ++ ModCustomCommand customCommand = new ModCustomCommand(par1ICommand.getCommandName()); ++ customCommand.setPermission(permissionNode); + if (list != null) ++ customCommand.setAliases(list); ++ commandMap.register(par1ICommand.getCommandName(), customCommand); ++ LogManager.getLogger().info("Registered command " + par1ICommand.getCommandName() + " with permission node " + permissionNode); ++ ++ if (list != null) + { + Iterator iterator = list.iterator(); + +@@ -149,13 +173,14 @@ + + if (icommand1 == null || !icommand1.getCommandName().equals(s)) + { +- this.commandMap.put(s, p_71560_1_); ++ this.commandMap.put(s, par1ICommand); + } + } + } + +- return p_71560_1_; ++ return par1ICommand; + } ++ // Cauldron end + + private static String[] dropFirstString(String[] p_71559_0_) + { diff --git a/patches/net/minecraft/command/PlayerSelector.java.patch b/patches/net/minecraft/command/PlayerSelector.java.patch new file mode 100644 index 0000000..b9a88e1 --- /dev/null +++ b/patches/net/minecraft/command/PlayerSelector.java.patch @@ -0,0 +1,24 @@ +--- ../src-base/minecraft/net/minecraft/command/PlayerSelector.java ++++ ../src-work/minecraft/net/minecraft/command/PlayerSelector.java +@@ -7,6 +7,7 @@ + import java.util.Map; + import java.util.regex.Matcher; + import java.util.regex.Pattern; ++import net.minecraft.command.server.CommandBlockLogic; + import net.minecraft.entity.player.EntityPlayerMP; + import net.minecraft.server.MinecraftServer; + import net.minecraft.util.ChunkCoordinates; +@@ -51,6 +52,13 @@ + + public static EntityPlayerMP[] matchPlayers(ICommandSender p_82380_0_, String p_82380_1_) + { ++ // CraftBukkit start ++ if (!(p_82380_0_ instanceof CommandBlockLogic)) ++ { ++ return null; ++ } ++ ++ // CraftBukkit end + Matcher matcher = tokenPattern.matcher(p_82380_1_); + + if (matcher.matches()) diff --git a/patches/net/minecraft/command/ServerCommandManager.java.patch b/patches/net/minecraft/command/ServerCommandManager.java.patch new file mode 100644 index 0000000..93d575e --- /dev/null +++ b/patches/net/minecraft/command/ServerCommandManager.java.patch @@ -0,0 +1,76 @@ +--- ../src-base/minecraft/net/minecraft/command/ServerCommandManager.java ++++ ../src-work/minecraft/net/minecraft/command/ServerCommandManager.java +@@ -39,8 +39,16 @@ + { + private static final String __OBFID = "CL_00000922"; + ++ // Cauldron start - moved commands to it's own method to be executed further in server startup + changed to registerVanillaCommand + public ServerCommandManager() + { ++ CommandBase.setAdminCommander(this); ++ } ++ ++ public void registerVanillaCommands() ++ { ++ // Cauldron - do not register vanilla commands replaced by Bukkit ++ /* + this.registerCommand(new CommandTime()); + this.registerCommand(new CommandGameMode()); + this.registerCommand(new CommandDifficulty()); +@@ -56,7 +64,6 @@ + this.registerCommand(new CommandEmote()); + this.registerCommand(new CommandShowSeed()); + this.registerCommand(new CommandHelp()); +- this.registerCommand(new CommandDebug()); + this.registerCommand(new CommandMessage()); + this.registerCommand(new CommandBroadcast()); + this.registerCommand(new CommandSetSpawnpoint()); +@@ -64,17 +71,23 @@ + this.registerCommand(new CommandGameRule()); + this.registerCommand(new CommandClearInventory()); + this.registerCommand(new CommandTestFor()); +- this.registerCommand(new CommandSpreadPlayers()); +- this.registerCommand(new CommandPlaySound()); +- this.registerCommand(new CommandScoreboard()); +- this.registerCommand(new CommandAchievement()); +- this.registerCommand(new CommandSummon()); +- this.registerCommand(new CommandSetBlock()); +- this.registerCommand(new CommandTestForBlock()); +- this.registerCommand(new CommandMessageRaw()); ++ */ ++ // Cauldron start - add permission nodes for rest of vanilla commands ++ this.registerCommand("vanilla.command", new CommandDebug()); ++ this.registerCommand("vanilla.command", new CommandSpreadPlayers()); ++ this.registerCommand("vanilla.command", new CommandPlaySound()); ++ this.registerCommand("vanilla.command", new CommandScoreboard()); ++ this.registerCommand("vanilla.command", new CommandAchievement()); ++ this.registerCommand("vanilla.command", new CommandSummon()); ++ this.registerCommand("vanilla.command", new CommandSetBlock()); ++ this.registerCommand("vanilla.command", new CommandTestForBlock()); ++ this.registerCommand("vanilla.command", new CommandMessageRaw()); ++ this.registerCommand("vanilla.command", new CommandNetstat()); ++ // Cauldron end + + if (MinecraftServer.getServer().isDedicatedServer()) + { ++ /* + this.registerCommand(new CommandOp()); + this.registerCommand(new CommandDeOp()); + this.registerCommand(new CommandStop()); +@@ -90,7 +103,7 @@ + this.registerCommand(new CommandListPlayers()); + this.registerCommand(new CommandWhitelist()); + this.registerCommand(new CommandSetPlayerTimeout()); +- this.registerCommand(new CommandNetstat()); ++ */ + } + else + { +@@ -98,6 +111,7 @@ + } + + CommandBase.setAdminCommander(this); ++ // Cauldron end + } + + public void func_152372_a(ICommandSender p_152372_1_, ICommand p_152372_2_, int p_152372_3_, String p_152372_4_, Object ... p_152372_5_) diff --git a/patches/net/minecraft/command/server/CommandBlockLogic.java.patch b/patches/net/minecraft/command/server/CommandBlockLogic.java.patch new file mode 100644 index 0000000..e80fed8 --- /dev/null +++ b/patches/net/minecraft/command/server/CommandBlockLogic.java.patch @@ -0,0 +1,212 @@ +--- ../src-base/minecraft/net/minecraft/command/server/CommandBlockLogic.java ++++ ../src-work/minecraft/net/minecraft/command/server/CommandBlockLogic.java +@@ -9,18 +9,30 @@ + import net.minecraft.command.ICommandSender; + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.server.MinecraftServer; ++import net.minecraft.tileentity.TileEntityCommandBlockListener; + import net.minecraft.util.ChatComponentText; + import net.minecraft.util.IChatComponent; + import net.minecraft.world.World; + ++// CraftBukkit start ++import java.util.ArrayList; ++import org.apache.logging.log4j.Level; ++import org.bukkit.craftbukkit.command.VanillaCommandWrapper; ++import com.google.common.base.Joiner; ++import net.minecraft.command.PlayerSelector; ++import net.minecraft.entity.EntityMinecartCommandBlockListener; ++import net.minecraft.entity.player.EntityPlayerMP; ++// CraftBukkit end ++ + public abstract class CommandBlockLogic implements ICommandSender + { + private static final SimpleDateFormat field_145766_a = new SimpleDateFormat("HH:mm:ss"); + private int field_145764_b; + private boolean field_145765_c = true; + private IChatComponent field_145762_d = null; +- private String field_145763_e = ""; ++ public String field_145763_e = ""; // CraftBukkit - private -> public + private String field_145761_f = "@"; ++ protected org.bukkit.command.CommandSender sender; // CraftBukkit - add sender; + private static final String __OBFID = "CL_00000128"; + + public int func_145760_g() +@@ -94,8 +106,143 @@ + + if (minecraftserver != null && minecraftserver.isCommandBlockEnabled()) + { +- ICommandManager icommandmanager = minecraftserver.getCommandManager(); +- this.field_145764_b = icommandmanager.executeCommand(this, this.field_145763_e); ++ // CraftBukkit start - Handle command block commands using Bukkit dispatcher ++ org.bukkit.command.SimpleCommandMap commandMap = minecraftserver.server.getCommandMap(); ++ Joiner joiner = Joiner.on(" "); ++ String command = this.field_145763_e; ++ ++ if (this.field_145763_e.startsWith("/")) ++ { ++ command = this.field_145763_e.substring(1); ++ } ++ ++ String[] args = command.split(" "); ++ ArrayList commands = new ArrayList(); ++ ++ // Block disallowed commands ++ if (args[0].equalsIgnoreCase("stop") || args[0].equalsIgnoreCase("kick") || args[0].equalsIgnoreCase("op") || ++ args[0].equalsIgnoreCase("deop") || args[0].equalsIgnoreCase("ban") || args[0].equalsIgnoreCase("ban-ip") || ++ args[0].equalsIgnoreCase("pardon") || args[0].equalsIgnoreCase("pardon-ip") || args[0].equalsIgnoreCase("reload")) ++ { ++ this.field_145764_b = 0; ++ return; ++ } ++ ++ // If the world has no players don't run ++ if (this.getEntityWorld().playerEntities.isEmpty()) ++ { ++ this.field_145764_b = 0; ++ return; ++ } ++ ++ // Handle vanilla commands; ++ if (minecraftserver.server.getCommandBlockOverride(args[0])) ++ { ++ org.bukkit.command.Command commandBlockCommand = commandMap.getCommand("minecraft:" + args[0]); ++ ++ if (commandBlockCommand instanceof VanillaCommandWrapper) ++ { ++ this.field_145764_b = ((VanillaCommandWrapper) commandBlockCommand).dispatchVanillaCommandBlock(this, this.field_145763_e); ++ return; ++ } ++ } ++ ++ // Make sure this is a valid command ++ if (commandMap.getCommand(args[0]) == null) ++ { ++ // Cauldron start - execute command using the vanilla command manager if it isn't in the bukkit command map ++ net.minecraft.command.ICommandManager icommandmanager = minecraftserver.getCommandManager(); ++ icommandmanager.executeCommand(this, this.field_145763_e); ++ return; ++ // Cauldron end ++ } ++ ++ // testfor command requires special handling ++ if (args[0].equalsIgnoreCase("testfor")) ++ { ++ if (args.length < 2) ++ { ++ this.field_145764_b = 0; ++ return; ++ } ++ ++ EntityPlayerMP[] players = PlayerSelector.matchPlayers(this, args[1]); ++ ++ if (players != null && players.length > 0) ++ { ++ this.field_145764_b = players.length; ++ return; ++ } ++ else ++ { ++ EntityPlayerMP player = MinecraftServer.getServer().getConfigurationManager().func_152612_a(args[1]); ++ ++ if (player == null) ++ { ++ this.field_145764_b = 0; ++ return; ++ } ++ else ++ { ++ this.field_145764_b = 1; ++ return; ++ } ++ } ++ } ++ ++ commands.add(args); ++ // Find positions of command block syntax, if any ++ ArrayList newCommands = new ArrayList(); ++ ++ for (int i = 0; i < args.length; i++) ++ { ++ if (PlayerSelector.hasArguments(args[i])) ++ { ++ for (int j = 0; j < commands.size(); j++) ++ { ++ newCommands.addAll(this.buildCommands(commands.get(j), i)); ++ } ++ ++ ArrayList temp = commands; ++ commands = newCommands; ++ newCommands = temp; ++ newCommands.clear(); ++ } ++ } ++ ++ int completed = 0; ++ ++ // Now dispatch all of the commands we ended up with ++ for (int i = 0; i < commands.size(); i++) ++ { ++ try ++ { ++ if (commandMap.dispatch(sender, joiner.join(java.util.Arrays.asList(commands.get(i))))) ++ { ++ completed++; ++ } ++ } ++ catch (Throwable exception) ++ { ++ if (this instanceof TileEntityCommandBlockListener) ++ { ++ TileEntityCommandBlockListener listener = (TileEntityCommandBlockListener) this; ++ MinecraftServer.getLogger().log(Level.WARN, String.format("CommandBlock at (%d,%d,%d) failed to handle command", listener.getPlayerCoordinates().posX, listener.getPlayerCoordinates().posY, listener.getPlayerCoordinates().posZ), exception); ++ } ++ else if (this instanceof EntityMinecartCommandBlockListener) ++ { ++ EntityMinecartCommandBlockListener listener = (EntityMinecartCommandBlockListener) this; ++ MinecraftServer.getLogger().log(Level.WARN, String.format("MinecartCommandBlock at (%d,%d,%d) failed to handle command", listener.getPlayerCoordinates().posX, listener.getPlayerCoordinates().posY, listener.getPlayerCoordinates().posZ), exception); ++ } ++ else ++ { ++ MinecraftServer.getLogger().log(Level.WARN, String.format("Unknown CommandBlock failed to handle command"), exception); ++ } ++ } ++ } ++ ++ this.field_145764_b = completed; ++ // CraftBukkit end + } + else + { +@@ -103,6 +250,31 @@ + } + } + ++ // CraftBukkit start ++ private ArrayList buildCommands(String[] args, int pos) ++ { ++ ArrayList commands = new ArrayList(); ++ EntityPlayerMP[] players = PlayerSelector.matchPlayers(this, args[pos]); ++ ++ if (players != null) ++ { ++ for (EntityPlayerMP player : players) ++ { ++ if (player.worldObj != this.getEntityWorld()) ++ { ++ continue; ++ } ++ ++ String[] command = args.clone(); ++ command[pos] = player.getCommandSenderName(); ++ commands.add(command); ++ } ++ } ++ ++ return commands; ++ } ++ // CraftBukkit end ++ + public String getCommandSenderName() + { + return this.field_145761_f; diff --git a/patches/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java.patch b/patches/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java.patch new file mode 100644 index 0000000..4222be5 --- /dev/null +++ b/patches/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java.patch @@ -0,0 +1,101 @@ +--- ../src-base/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java ++++ ../src-work/minecraft/net/minecraft/dispenser/BehaviorDefaultDispenseItem.java +@@ -6,6 +6,11 @@ + import net.minecraft.util.EnumFacing; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + public class BehaviorDefaultDispenseItem implements IBehaviorDispenseItem + { + private static final String __OBFID = "CL_00001195"; +@@ -23,10 +28,17 @@ + EnumFacing enumfacing = BlockDispenser.func_149937_b(p_82487_1_.getBlockMetadata()); + IPosition iposition = BlockDispenser.func_149939_a(p_82487_1_); + ItemStack itemstack1 = p_82487_2_.splitStack(1); +- doDispense(p_82487_1_.getWorld(), itemstack1, 6, enumfacing, iposition); ++ // CraftBukkit start ++ if (!doDispense(p_82487_1_.getWorld(), itemstack1, 6, enumfacing, p_82487_1_)) ++ { ++ p_82487_2_.stackSize++; ++ } ++ ++ // CraftBukkit end + return p_82487_2_; + } + ++ // Cauldron start - vanilla compatibility + public static void doDispense(World p_82486_0_, ItemStack p_82486_1_, int p_82486_2_, EnumFacing p_82486_3_, IPosition p_82486_4_) + { + double d0 = p_82486_4_.getX(); +@@ -42,7 +54,67 @@ + entityitem.motionZ += p_82486_0_.rand.nextGaussian() * 0.007499999832361937D * (double)p_82486_2_; + p_82486_0_.spawnEntityInWorld(entityitem); + } ++ // Cauldron end + ++ // CraftBukkit start - void -> boolean return, IPosition -> ISourceBlock last argument ++ public static boolean doDispense(World world, ItemStack itemstack, int i, EnumFacing enumfacing, IBlockSource iblocksource) ++ { ++ IPosition iposition = BlockDispenser.func_149939_a(iblocksource); ++ // CraftBukkit end ++ double d0 = iposition.getX(); ++ double d1 = iposition.getY(); ++ double d2 = iposition.getZ(); ++ EntityItem entityitem = new EntityItem(world, d0, d1 - 0.3D, d2, itemstack); ++ double d3 = world.rand.nextDouble() * 0.1D + 0.2D; ++ entityitem.motionX = (double) enumfacing.getFrontOffsetX() * d3; ++ entityitem.motionY = 0.20000000298023224D; ++ entityitem.motionZ = (double) enumfacing.getFrontOffsetZ() * d3; ++ entityitem.motionX += world.rand.nextGaussian() * 0.007499999832361937D * (double) i; ++ entityitem.motionY += world.rand.nextGaussian() * 0.007499999832361937D * (double) i; ++ entityitem.motionZ += world.rand.nextGaussian() * 0.007499999832361937D * (double) i; ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(iblocksource.getXInt(), iblocksource.getYInt(), iblocksource.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(entityitem.motionX, entityitem.motionY, entityitem.motionZ)); ++ ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ return false; ++ } ++ ++ entityitem.setEntityItemStack(CraftItemStack.asNMSCopy(event.getItem())); ++ entityitem.motionX = event.getVelocity().getX(); ++ entityitem.motionY = event.getVelocity().getY(); ++ entityitem.motionZ = event.getVelocity().getZ(); ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem.getClass() != BehaviorDefaultDispenseItem.class) ++ { ++ ibehaviordispenseitem.dispense(iblocksource, eventStack); ++ } ++ else ++ { ++ world.spawnEntityInWorld(entityitem); ++ } ++ ++ return false; ++ } ++ ++ world.spawnEntityInWorld(entityitem); ++ return true; ++ // CraftBukkit end ++ } ++ + protected void playDispenseSound(IBlockSource p_82485_1_) + { + p_82485_1_.getWorld().playAuxSFX(1000, p_82485_1_.getXInt(), p_82485_1_.getYInt(), p_82485_1_.getZInt(), 0); diff --git a/patches/net/minecraft/dispenser/BehaviorProjectileDispense.java.patch b/patches/net/minecraft/dispenser/BehaviorProjectileDispense.java.patch new file mode 100644 index 0000000..8c44a01 --- /dev/null +++ b/patches/net/minecraft/dispenser/BehaviorProjectileDispense.java.patch @@ -0,0 +1,59 @@ +--- ../src-base/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.java ++++ ../src-work/minecraft/net/minecraft/dispenser/BehaviorProjectileDispense.java +@@ -7,6 +7,12 @@ + import net.minecraft.util.EnumFacing; + import net.minecraft.world.World; + ++// CraftBukkit start ++import net.minecraft.tileentity.TileEntityDispenser; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + public abstract class BehaviorProjectileDispense extends BehaviorDefaultDispenseItem + { + private static final String __OBFID = "CL_00001394"; +@@ -17,9 +23,42 @@ + IPosition iposition = BlockDispenser.func_149939_a(p_82487_1_); + EnumFacing enumfacing = BlockDispenser.func_149937_b(p_82487_1_.getBlockMetadata()); + IProjectile iprojectile = this.getProjectileEntity(world, iposition); ++ // CraftBukkit start ++ ItemStack itemstack1 = p_82487_2_.splitStack(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) enumfacing.getFrontOffsetX(), (double)((float) enumfacing.getFrontOffsetY() + 0.1F), (double) enumfacing.getFrontOffsetZ())); ++ ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ p_82487_2_.stackSize++; ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ p_82487_2_.stackSize++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ + iprojectile.setThrowableHeading((double)enumfacing.getFrontOffsetX(), (double)((float)enumfacing.getFrontOffsetY() + 0.1F), (double)enumfacing.getFrontOffsetZ(), this.func_82500_b(), this.func_82498_a()); ++ ((Entity) iprojectile).projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) p_82487_1_.getBlockTileEntity()); ++ // CraftBukkit end + world.spawnEntityInWorld((Entity)iprojectile); +- p_82487_2_.splitStack(1); ++ // p_82487_2_.splitStack(1); // CraftBukkit - Handled during event processing + return p_82487_2_; + } + diff --git a/patches/net/minecraft/enchantment/Enchantment.java.patch b/patches/net/minecraft/enchantment/Enchantment.java.patch new file mode 100644 index 0000000..0bb4011 --- /dev/null +++ b/patches/net/minecraft/enchantment/Enchantment.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/enchantment/Enchantment.java ++++ ../src-work/minecraft/net/minecraft/enchantment/Enchantment.java +@@ -56,6 +56,8 @@ + { + enchantmentsList[p_i1926_1_] = this; + } ++ ++ org.bukkit.enchantments.Enchantment.registerEnchantment(new org.bukkit.craftbukkit.enchantments.CraftEnchantment(this)); // CraftBukkit + } + + public int getWeight() diff --git a/patches/net/minecraft/entity/Entity.java.patch b/patches/net/minecraft/entity/Entity.java.patch new file mode 100644 index 0000000..2eb1107 --- /dev/null +++ b/patches/net/minecraft/entity/Entity.java.patch @@ -0,0 +1,775 @@ +--- ../src-base/minecraft/net/minecraft/entity/Entity.java ++++ ../src-work/minecraft/net/minecraft/entity/Entity.java +@@ -45,13 +45,51 @@ + import net.minecraft.world.Explosion; + import net.minecraft.world.World; + import net.minecraft.world.WorldServer; ++import net.minecraftforge.cauldron.CauldronHooks; + import net.minecraftforge.common.IExtendedEntityProperties; + import net.minecraftforge.common.MinecraftForge; + import net.minecraftforge.event.entity.EntityEvent; + import net.minecraftforge.fluids.IFluidBlock; + ++// CraftBukkit start ++import net.minecraft.entity.passive.EntityTameable; ++import net.minecraft.entity.player.EntityPlayerMP; ++import org.bukkit.Bukkit; ++import org.bukkit.Location; ++import org.bukkit.Server; ++import org.bukkit.TravelAgent; ++import org.bukkit.block.BlockFace; ++import org.bukkit.entity.Hanging; ++import org.bukkit.entity.LivingEntity; ++import org.bukkit.entity.Painting; ++import org.bukkit.entity.Vehicle; ++import org.spigotmc.CustomTimingsHandler; // Spigot ++import org.bukkit.event.entity.EntityCombustByEntityEvent; ++import org.bukkit.event.painting.PaintingBreakByEntityEvent; ++import org.bukkit.event.vehicle.VehicleBlockCollisionEvent; ++import org.bukkit.event.vehicle.VehicleEnterEvent; ++import org.bukkit.event.vehicle.VehicleExitEvent; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityCombustEvent; ++import org.bukkit.event.entity.EntityPortalEvent; ++import org.bukkit.event.hanging.HangingBreakByEntityEvent; ++import org.bukkit.plugin.PluginManager; ++// CraftBukkit end ++import net.minecraft.world.Teleporter; // Cauldron ++ + public abstract class Entity + { ++ // CraftBukkit start ++ private static final int CURRENT_LEVEL = 2; ++ static boolean isLevelAtLeast(NBTTagCompound tag, int level) ++ { ++ return tag.hasKey("Bukkit.updateLevel") && tag.getInteger("Bukkit.updateLevel") >= level; ++ } ++ // CraftBukkit end ++ + private static int nextEntityID; + private int entityId; + public double renderDistanceWeight; +@@ -100,12 +138,12 @@ + protected Random rand; + public int ticksExisted; + public int fireResistance; +- private int fire; +- protected boolean inWater; ++ public int fire; // CraftBukkit - private -> public ++ public boolean inWater; // Spigot - protected -> public + public int hurtResistantTime; + private boolean firstUpdate; + protected boolean isImmuneToFire; +- protected DataWatcher dataWatcher; ++ public DataWatcher dataWatcher; // CraftBukkit - protected -> public + private double entityRiderPitchDelta; + private double entityRiderYawDelta; + public boolean addedToChunk; +@@ -126,8 +164,10 @@ + public int dimension; + protected int teleportDirection; + private boolean invulnerable; +- protected UUID entityUniqueID; ++ public UUID entityUniqueID; // CraftBukkit - protected -> public + public Entity.EnumEntitySize myEntitySize; ++ public boolean valid; // CraftBukkit ++ public org.bukkit.projectiles.ProjectileSource projectileSource; // CraftBukkit - For projectiles only + private static final String __OBFID = "CL_00001533"; + /** Forge: Used to store custom data for each entity. */ + private NBTTagCompound customEntityData; +@@ -135,7 +175,16 @@ + public ArrayList capturedDrops = new ArrayList(); + private UUID persistentID; + ++ // Spigot start ++ public CustomTimingsHandler tickTimer = org.bukkit.craftbukkit.SpigotTimings.getEntityTimings(this); // Spigot ++ public final byte activationType = org.spigotmc.ActivationRange.initializeEntityActivationType(this); ++ public final boolean defaultActivationState; ++ public long activatedTick = 0; ++ public boolean fromMobSpawner; ++ public void inactiveTick() { } ++ // Spigot end + protected HashMap extendedProperties; ++ public String spawnReason; // Cauldron - used to handle CraftBukkit's SpawnReason with CustomSpawners + + public int getEntityId() + { +@@ -159,7 +208,7 @@ + this.rand = new Random(); + this.fireResistance = 1; + this.firstUpdate = true; +- this.entityUniqueID = UUID.randomUUID(); ++ this.entityUniqueID = new UUID(rand.nextLong(), rand.nextLong()); // Spigot + this.myEntitySize = Entity.EnumEntitySize.SIZE_2; + this.worldObj = p_i1582_1_; + this.setPosition(0.0D, 0.0D, 0.0D); +@@ -167,8 +216,15 @@ + if (p_i1582_1_ != null) + { + this.dimension = p_i1582_1_.provider.dimensionId; ++ // Spigot start ++ this.defaultActivationState = org.spigotmc.ActivationRange.initializeEntityActivationState(this, p_i1582_1_.getSpigotConfig()); // Cauldron + } ++ else ++ { ++ this.defaultActivationState = false; ++ } + ++ // Spigot end + this.dataWatcher = new DataWatcher(this); + this.dataWatcher.addObject(0, Byte.valueOf((byte)0)); + this.dataWatcher.addObject(1, Short.valueOf((short)300)); +@@ -277,6 +333,41 @@ + + protected void setRotation(float p_70101_1_, float p_70101_2_) + { ++ // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0 ++ if (Float.isNaN(p_70101_1_)) ++ { ++ p_70101_1_ = 0; ++ } ++ ++ if ((p_70101_1_ == Float.POSITIVE_INFINITY) || (p_70101_1_ == Float.NEGATIVE_INFINITY)) ++ { ++ if (this instanceof EntityPlayerMP) ++ { ++ this.worldObj.getServer().getLogger().warning(((CraftPlayer) this.getBukkitEntity()).getName() + " was caught trying to crash the server with an invalid yaw"); ++ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Nope"); ++ } ++ ++ p_70101_1_ = 0; ++ } ++ ++ // pitch was sometimes set to NaN, so we need to set it back to 0. ++ if (Float.isNaN(p_70101_2_)) ++ { ++ p_70101_2_ = 0; ++ } ++ ++ if ((p_70101_2_ == Float.POSITIVE_INFINITY) || (p_70101_2_ == Float.NEGATIVE_INFINITY)) ++ { ++ if (this instanceof EntityPlayerMP) ++ { ++ this.worldObj.getServer().getLogger().warning(((CraftPlayer) this.getBukkitEntity()).getName() + " was caught trying to crash the server with an invalid pitch"); ++ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Nope"); ++ } ++ ++ p_70101_2_ = 0; ++ } ++ ++ // CraftBukkit end + this.rotationYaw = p_70101_1_ % 360.0F; + this.rotationPitch = p_70101_2_ % 360.0F; + } +@@ -343,7 +434,7 @@ + + if (this.inPortal) + { +- if (minecraftserver.getAllowNether()) ++ if (true || minecraftserver.getAllowNether()) // CraftBukkit + { + if (this.ridingEntity == null && this.portalCounter++ >= i) + { +@@ -457,7 +548,35 @@ + { + if (!this.isImmuneToFire) + { +- this.attackEntityFrom(DamageSource.lava, 4.0F); ++ // CraftBukkit start - Fallen in lava TODO: this event spams! ++ this.attackEntityFrom(DamageSource.lava, 4); ++ ++ if (this instanceof EntityLivingBase) ++ { ++ if (this.fire <= 0) ++ { ++ // not on fire yet ++ // TODO: shouldn't be sending null for the block. ++ org.bukkit.block.Block damager = null; // ((WorldServer) this.l).getWorld().getBlockAt(i, j, k); ++ org.bukkit.entity.Entity damagee = this.getBukkitEntity(); ++ EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15); ++ this.worldObj.getServer().getPluginManager().callEvent(combustEvent); ++ ++ if (!combustEvent.isCancelled()) ++ { ++ this.setFire(combustEvent.getDuration()); ++ } ++ } ++ else ++ { ++ // This will be called every single tick the entity is in lava, so don't throw an event ++ this.setFire(15); ++ } ++ ++ return; ++ } ++ ++ // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls + this.setFire(15); + } + } +@@ -492,6 +611,30 @@ + + public void moveEntity(double p_70091_1_, double p_70091_3_, double p_70091_5_) + { ++ // CraftBukkit start - Don't do anything if we aren't moving ++ // We need to do this regardless of whether or not we are moving thanks to portals ++ try ++ { ++ this.func_145775_I(); ++ } ++ catch (Throwable throwable) ++ { ++ CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Checking entity block collision"); ++ CrashReportCategory crashreportcategory = crashreport.makeCategory("Entity being checked for collision"); ++ this.addEntityCrashInfo(crashreportcategory); ++ throw new ReportedException(crashreport); ++ } ++ ++ // Check if we're moving ++ if (p_70091_1_ == 0 && p_70091_3_ == 0 && p_70091_5_ == 0 && this.ridingEntity == null && this.riddenByEntity == null) ++ { ++ return; ++ } ++ ++ // CraftBukkit end ++ if (!CauldronHooks.checkEntitySpeed(this, p_70091_1_, p_70091_3_, p_70091_5_)) return; // Check for entities violating the speed limit ++ org.bukkit.craftbukkit.SpigotTimings.entityMoveTimer.startTiming(); // Spigot ++ + if (this.noClip) + { + this.boundingBox.offset(p_70091_1_, p_70091_3_, p_70091_5_); +@@ -756,6 +899,35 @@ + d10 = this.posY - d4; + d11 = this.posZ - d5; + ++ // CraftBukkit start ++ if ((this.isCollidedHorizontally) && (this.getBukkitEntity() instanceof Vehicle)) ++ { ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ org.bukkit.block.Block block = this.worldObj.getWorld().getBlockAt(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY - (double) this.yOffset), MathHelper.floor_double(this.posZ)); ++ ++ if (d6 > p_70091_1_) ++ { ++ block = block.getRelative(BlockFace.EAST); ++ } ++ else if (d6 < p_70091_1_) ++ { ++ block = block.getRelative(BlockFace.WEST); ++ } ++ else if (d8 > p_70091_5_) ++ { ++ block = block.getRelative(BlockFace.SOUTH); ++ } ++ else if (d8 < p_70091_5_) ++ { ++ block = block.getRelative(BlockFace.NORTH); ++ } ++ ++ VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, block); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ } ++ ++ // CraftBukkit end ++ + if (this.canTriggerWalking() && !flag && this.ridingEntity == null) + { + int j1 = MathHelper.floor_double(this.posX); +@@ -798,6 +970,8 @@ + } + } + ++ // CraftBukkit start - Move to the top of the method ++ /* + try + { + this.func_145775_I(); +@@ -809,7 +983,8 @@ + this.addEntityCrashInfo(crashreportcategory); + throw new ReportedException(crashreport); + } +- ++ */ ++ // CraftBukkit end + boolean flag2 = this.isWet(); + + if (this.worldObj.func_147470_e(this.boundingBox.contract(0.001D, 0.001D, 0.001D))) +@@ -820,8 +995,20 @@ + { + ++this.fire; + +- if (this.fire == 0) ++ // CraftBukkit start - Not on fire yet ++ if (this.fire <= 0) // Only throw events on the first combust, otherwise it spams + { ++ EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), 8); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.setFire(event.getDuration()); ++ } ++ } ++ else ++ { ++ // CraftBukkit end + this.setFire(8); + } + } +@@ -839,6 +1026,8 @@ + + this.worldObj.theProfiler.endSection(); + } ++ ++ org.bukkit.craftbukkit.SpigotTimings.entityMoveTimer.stopTiming(); // Spigot + } + + protected String getSwimSound() +@@ -867,7 +1056,11 @@ + + try + { ++ // Cauldron start - damage hook for custom blocks ++ CraftEventFactory.blockDamage = this.worldObj.getWorld().getBlockAt(k1, l1, i2); + block.onEntityCollidedWithBlock(this.worldObj, k1, l1, i2, this); ++ CraftEventFactory.blockDamage = null; ++ // Cauldron end + } + catch (Throwable throwable) + { +@@ -928,6 +1121,7 @@ + return null; + } + ++ // Cauldron start - vanilla compatibility + protected void dealFireDamage(int p_70081_1_) + { + if (!this.isImmuneToFire) +@@ -935,7 +1129,16 @@ + this.attackEntityFrom(DamageSource.inFire, (float)p_70081_1_); + } + } ++ // Cauldron end + ++ protected void dealFireDamage(float par1) // CraftBukkit signature change ++ { ++ if (!this.isImmuneToFire) ++ { ++ this.attackEntityFrom(DamageSource.inFire, (float)par1); ++ } ++ } ++ + public final boolean isImmuneToFire() + { + return this.isImmuneToFire; +@@ -1184,6 +1387,8 @@ + + public void onCollideWithPlayer(EntityPlayer p_70100_1_) {} + ++ int numCollisions = 0; // Spigot ++ + public void applyEntityCollision(Entity p_70108_1_) + { + if (p_70108_1_.riddenByEntity != this && p_70108_1_.ridingEntity != this) +@@ -1310,6 +1515,20 @@ + { + p_70109_1_.setTag("Pos", this.newDoubleNBTList(new double[] {this.posX, this.posY + (double)this.ySize, this.posZ})); + p_70109_1_.setTag("Motion", this.newDoubleNBTList(new double[] {this.motionX, this.motionY, this.motionZ})); ++ ++ // CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero ++ // TODO: make sure this is the best way to address this. ++ if (Float.isNaN(this.rotationYaw)) ++ { ++ this.rotationYaw = 0; ++ } ++ ++ if (Float.isNaN(this.rotationPitch)) ++ { ++ this.rotationPitch = 0; ++ } ++ ++ // CraftBukkit end + p_70109_1_.setTag("Rotation", this.newFloatNBTList(new float[] {this.rotationYaw, this.rotationPitch})); + p_70109_1_.setFloat("FallDistance", this.fallDistance); + p_70109_1_.setShort("Fire", (short)this.fire); +@@ -1320,6 +1539,12 @@ + p_70109_1_.setInteger("PortalCooldown", this.timeUntilPortal); + p_70109_1_.setLong("UUIDMost", this.getUniqueID().getMostSignificantBits()); + p_70109_1_.setLong("UUIDLeast", this.getUniqueID().getLeastSignificantBits()); ++ // CraftBukkit start ++ p_70109_1_.setLong("WorldUUIDLeast", this.worldObj.getSaveHandler().getUUID().getLeastSignificantBits()); ++ p_70109_1_.setLong("WorldUUIDMost", this.worldObj.getSaveHandler().getUUID().getMostSignificantBits()); ++ p_70109_1_.setInteger("Bukkit.updateLevel", CURRENT_LEVEL); ++ p_70109_1_.setInteger("Spigot.ticksLived", this.ticksExisted); ++ // CraftBukkit end + if (customEntityData != null) + { + p_70109_1_.setTag("ForgeData", customEntityData); +@@ -1370,7 +1595,7 @@ + this.motionX = nbttaglist1.func_150309_d(0); + this.motionY = nbttaglist1.func_150309_d(1); + this.motionZ = nbttaglist1.func_150309_d(2); +- ++ /* CraftBukkit start - Moved section down + if (Math.abs(this.motionX) > 10.0D) + { + this.motionX = 0.0D; +@@ -1385,7 +1610,7 @@ + { + this.motionZ = 0.0D; + } +- ++ // CraftBukkit end */ + this.prevPosX = this.lastTickPosX = this.posX = nbttaglist.func_150309_d(0); + this.prevPosY = this.lastTickPosY = this.posY = nbttaglist.func_150309_d(1); + this.prevPosZ = this.lastTickPosZ = this.posZ = nbttaglist.func_150309_d(2); +@@ -1436,6 +1661,76 @@ + { + this.setPosition(this.posX, this.posY, this.posZ); + } ++ ++ // CraftBukkit start ++ if (this instanceof EntityLivingBase) ++ { ++ EntityLivingBase entity = (EntityLivingBase) this; ++ this.ticksExisted = p_70020_1_.getInteger("Spigot.ticksLived"); ++ ++ // Reset the persistence for tamed animals ++ if (entity instanceof EntityTameable && !isLevelAtLeast(p_70020_1_, 2) && !p_70020_1_.getBoolean("PersistenceRequired")) ++ { ++ EntityLiving entityliving = (EntityLiving) entity; ++ entityliving.persistenceRequired = !entityliving.canDespawn(); ++ } ++ } ++ ++ // CraftBukkit end ++ ++ // CraftBukkit start - Exempt Vehicles from notch's sanity check ++ if (!(this.getBukkitEntity() instanceof Vehicle)) ++ { ++ if (Math.abs(this.motionX) > 10.0D) ++ { ++ this.motionX = 0.0D; ++ } ++ ++ if (Math.abs(this.motionY) > 10.0D) ++ { ++ this.motionY = 0.0D; ++ } ++ ++ if (Math.abs(this.motionZ) > 10.0D) ++ { ++ this.motionZ = 0.0D; ++ } ++ } ++ ++ // CraftBukkit end ++ ++ // CraftBukkit start - Reset world ++ if (this instanceof EntityPlayerMP) ++ { ++ Server server = Bukkit.getServer(); ++ org.bukkit.World bworld = null; ++ // TODO: Remove World related checks, replaced with WorldUID. ++ String worldName = p_70020_1_.getString("World"); ++ ++ if (p_70020_1_.hasKey("WorldUUIDMost") && p_70020_1_.hasKey("WorldUUIDLeast")) ++ { ++ UUID uid = new UUID(p_70020_1_.getLong("WorldUUIDMost"), p_70020_1_.getLong("WorldUUIDLeast")); ++ bworld = server.getWorld(uid); ++ } ++ else ++ { ++ bworld = server.getWorld(worldName); ++ } ++ ++ if (bworld == null) ++ { ++ EntityPlayerMP entityPlayer = (EntityPlayerMP) this; ++ // Cauldron start - use CraftBukkit's fallback world code if no valid world is found. ++ entityPlayer.setWorld(MinecraftServer.getServer().worldServerForDimension(entityPlayer.dimension)); ++ } ++ else ++ { ++ this.setWorld(((CraftWorld) bworld).getHandle()); ++ // Cauldron end ++ } ++ } ++ ++ // CraftBukkit end + } + catch (Throwable throwable) + { +@@ -1653,6 +1948,31 @@ + + public void mountEntity(Entity p_70078_1_) + { ++ // CraftBukkit start ++ this.setPassengerOf(p_70078_1_); ++ } ++ ++ protected CraftEntity bukkitEntity; ++ ++ public CraftEntity getBukkitEntity() ++ { ++ if (this.bukkitEntity == null) ++ { ++ this.bukkitEntity = CraftEntity.getEntity(this.worldObj.getServer(), this); ++ } ++ ++ return this.bukkitEntity; ++ } ++ ++ public void setPassengerOf(Entity p_70078_1_) ++ { ++ // mountEntity(null) doesn't really fly for overloaded methods, ++ // so this method is needed ++ Entity originalVehicle = this.ridingEntity; ++ Entity originalPassenger = this.ridingEntity == null ? null : this.ridingEntity.riddenByEntity; ++ PluginManager pluginManager = Bukkit.getPluginManager(); ++ this.getBukkitEntity(); // make sure bukkitEntity is initialised ++ // CraftBukkit end + this.entityRiderPitchDelta = 0.0D; + this.entityRiderYawDelta = 0.0D; + +@@ -1660,6 +1980,20 @@ + { + if (this.ridingEntity != null) + { ++ // CraftBukkit start ++ if ((this.bukkitEntity instanceof LivingEntity) && (this.ridingEntity.getBukkitEntity() instanceof Vehicle)) ++ { ++ VehicleExitEvent event = new VehicleExitEvent((Vehicle) this.ridingEntity.getBukkitEntity(), (LivingEntity) this.bukkitEntity); ++ pluginManager.callEvent(event); ++ ++ if (event.isCancelled() || this.ridingEntity != originalVehicle) ++ { ++ return; ++ } ++ } ++ ++ // CraftBukkit end ++ pluginManager.callEvent(new org.spigotmc.event.entity.EntityDismountEvent(this.getBukkitEntity(), this.ridingEntity.getBukkitEntity())); // Spigot + this.setLocationAndAngles(this.ridingEntity.posX, this.ridingEntity.boundingBox.minY + (double)this.ridingEntity.height, this.ridingEntity.posZ, this.rotationYaw, this.rotationPitch); + this.ridingEntity.riddenByEntity = null; + } +@@ -1668,22 +2002,61 @@ + } + else + { +- if (this.ridingEntity != null) ++ // CraftBukkit start ++ if ((this.bukkitEntity instanceof LivingEntity) && (p_70078_1_.getBukkitEntity() instanceof Vehicle) && p_70078_1_.worldObj.chunkExists((int) p_70078_1_.posX >> 4, (int) p_70078_1_.posZ >> 4)) + { +- this.ridingEntity.riddenByEntity = null; +- } ++ // It's possible to move from one vehicle to another. We need to check if they're already in a vehicle, and fire an exit event if they are. ++ VehicleExitEvent exitEvent = null; + +- if (p_70078_1_ != null) +- { +- for (Entity entity1 = p_70078_1_.ridingEntity; entity1 != null; entity1 = entity1.ridingEntity) ++ if (this.ridingEntity != null && this.ridingEntity.getBukkitEntity() instanceof Vehicle) + { +- if (entity1 == this) ++ exitEvent = new VehicleExitEvent((Vehicle) this.ridingEntity.getBukkitEntity(), (LivingEntity) this.bukkitEntity); ++ pluginManager.callEvent(exitEvent); ++ ++ if (exitEvent.isCancelled() || this.ridingEntity != originalVehicle || (this.ridingEntity != null && this.ridingEntity.riddenByEntity != originalPassenger)) + { + return; + } + } ++ ++ VehicleEnterEvent event = new VehicleEnterEvent((Vehicle) p_70078_1_.getBukkitEntity(), this.bukkitEntity); ++ pluginManager.callEvent(event); ++ ++ // If a plugin messes with the vehicle or the vehicle's passenger ++ if (event.isCancelled() || this.ridingEntity != originalVehicle || (this.ridingEntity != null && this.ridingEntity.riddenByEntity != originalPassenger)) ++ { ++ // If we only cancelled the enterevent then we need to put the player in a decent position. ++ if (exitEvent != null && this.ridingEntity == originalVehicle && this.ridingEntity != null && this.ridingEntity.riddenByEntity == originalPassenger) ++ { ++ this.setLocationAndAngles(this.ridingEntity.posX, this.ridingEntity.boundingBox.minY + (double) this.ridingEntity.height, this.ridingEntity.posZ, this.rotationYaw, this.rotationPitch); ++ this.ridingEntity.riddenByEntity = null; ++ this.ridingEntity = null; ++ } ++ ++ return; ++ } + } + ++ // CraftBukkit end ++ // Spigot Start ++ if (p_70078_1_.worldObj.chunkExists((int) p_70078_1_.posX >> 4, (int) p_70078_1_.posZ >> 4)) ++ { ++ org.spigotmc.event.entity.EntityMountEvent event = new org.spigotmc.event.entity.EntityMountEvent(this.getBukkitEntity(), p_70078_1_.getBukkitEntity()); ++ pluginManager.callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ } ++ ++ // Spigot End ++ ++ if (this.ridingEntity != null) ++ { ++ this.ridingEntity.riddenByEntity = null; ++ } ++ + this.ridingEntity = p_70078_1_; + p_70078_1_.riddenByEntity = this; + } +@@ -1860,12 +2233,59 @@ + + public void onStruckByLightning(EntityLightningBolt p_70077_1_) + { +- this.dealFireDamage(5); ++ // CraftBukkit start ++ final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity(); ++ if (thisBukkitEntity == null) return; // Cauldron - skip mod entities with no wrapper (TODO: create a wrapper) ++ if (p_70077_1_ == null) return; // Cauldron - skip null entities, see #392 ++ final org.bukkit.entity.Entity stormBukkitEntity = p_70077_1_.getBukkitEntity(); ++ if (stormBukkitEntity == null) return; // Cauldron - skip mod entities with no wrapper (TODO: create a wrapper) ++ final PluginManager pluginManager = Bukkit.getPluginManager(); ++ ++ if (thisBukkitEntity instanceof Hanging) ++ { ++ HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity); ++ PaintingBreakByEntityEvent paintingEvent = null; ++ ++ if (thisBukkitEntity instanceof Painting) { ++ paintingEvent = new PaintingBreakByEntityEvent((Painting) thisBukkitEntity, stormBukkitEntity); ++ } ++ ++ pluginManager.callEvent(hangingEvent); ++ ++ if (paintingEvent != null) { ++ paintingEvent.setCancelled(hangingEvent.isCancelled()); ++ pluginManager.callEvent(paintingEvent); ++ } ++ ++ if (hangingEvent.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { ++ return; ++ } ++ } ++ ++ if (this.isImmuneToFire) { ++ return; ++ } ++ CraftEventFactory.entityDamage = p_70077_1_; ++ if (!this.attackEntityFrom(DamageSource.inFire, 5.0F)) { ++ CraftEventFactory.entityDamage = null; ++ return; ++ } ++ ++ // CraftBukkit end + ++this.fire; + + if (this.fire == 0) + { +- this.setFire(8); ++ // CraftBukkit start - Call a combust event when lightning strikes ++ EntityCombustByEntityEvent entityCombustEvent = new EntityCombustByEntityEvent(stormBukkitEntity, thisBukkitEntity, 8); ++ pluginManager.callEvent(entityCombustEvent); ++ ++ if (!entityCombustEvent.isCancelled()) ++ { ++ this.setFire(entityCombustEvent.getDuration()); ++ } ++ ++ // CraftBukkit end + } + } + +@@ -2038,36 +2458,62 @@ + { + this.worldObj.theProfiler.startSection("changeDimension"); + MinecraftServer minecraftserver = MinecraftServer.getServer(); +- int j = this.dimension; +- WorldServer worldserver = minecraftserver.worldServerForDimension(j); +- WorldServer worldserver1 = minecraftserver.worldServerForDimension(p_71027_1_); +- this.dimension = p_71027_1_; ++ // CraftBukkit start - Move logic into new function "teleportToLocation" ++ // int j = this.dimension; ++ // Cauldron start - Allow Forge hotloading on teleport ++ WorldServer exitWorld = minecraftserver.worldServerForDimension(p_71027_1_); + +- if (j == 1 && p_71027_1_ == 1) ++ Location enter = this.getBukkitEntity().getLocation(); ++ Location exit = exitWorld != null ? minecraftserver.getConfigurationManager().calculateTarget(enter, minecraftserver.worldServerForDimension(p_71027_1_)) : null; ++ boolean useTravelAgent = exitWorld != null && !(this.dimension == 1 && exitWorld.dimension == 1); // don't use agent for custom worlds or return from THE_END ++ // Cauldron start - check if teleporter is instance of TravelAgent before attempting to cast to it ++ Teleporter teleporter = exit != null ? ((CraftWorld) exit.getWorld()).getHandle().getDefaultTeleporter() : null; ++ TravelAgent agent = (teleporter != null && teleporter instanceof TravelAgent) ? (TravelAgent)teleporter : org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins ++ // Cauldron end ++ EntityPortalEvent event = new EntityPortalEvent(this.getBukkitEntity(), enter, exit, agent); ++ event.useTravelAgent(useTravelAgent); ++ event.getEntity().getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled() || event.getTo() == null || !this.isEntityAlive()) + { +- worldserver1 = minecraftserver.worldServerForDimension(0); +- this.dimension = 0; ++ return; + } + ++ exit = event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); ++ this.teleportTo(exit, true); ++ } ++ } ++ ++ public void teleportTo(Location exit, boolean portal) ++ { ++ if (true) ++ { ++ WorldServer worldserver = ((CraftWorld) this.getBukkitEntity().getLocation().getWorld()).getHandle(); ++ WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); ++ int i = worldserver1.dimension; ++ // CraftBukkit end ++ this.dimension = i; + this.worldObj.removeEntity(this); + this.isDead = false; + this.worldObj.theProfiler.startSection("reposition"); +- minecraftserver.getConfigurationManager().transferEntityToWorld(this, j, worldserver, worldserver1); ++ // CraftBukkit start - Ensure chunks are loaded in case TravelAgent is not used which would initially cause chunks to load during find/create ++ // minecraftserver.getPlayerList().a(this, j, worldserver, worldserver1); ++ boolean before = worldserver1.theChunkProviderServer.loadChunkOnProvideRequest; // Cauldron start - load chunks on provide request ++ worldserver1.theChunkProviderServer.loadChunkOnProvideRequest = true; ++ worldserver1.func_73046_m().getConfigurationManager().repositionEntity(this, exit, portal); ++ worldserver1.theChunkProviderServer.loadChunkOnProvideRequest = before; // Cauldron end ++ // CraftBukkit end + this.worldObj.theProfiler.endStartSection("reloading"); + Entity entity = EntityList.createEntityByName(EntityList.getEntityString(this), worldserver1); + + if (entity != null) + { + entity.copyDataFrom(this, true); +- +- if (j == 1 && p_71027_1_ == 1) +- { +- ChunkCoordinates chunkcoordinates = worldserver1.getSpawnPoint(); +- chunkcoordinates.posY = this.worldObj.getTopSolidOrLiquidBlock(chunkcoordinates.posX, chunkcoordinates.posZ); +- entity.setLocationAndAngles((double)chunkcoordinates.posX, (double)chunkcoordinates.posY, (double)chunkcoordinates.posZ, entity.rotationYaw, entity.rotationPitch); +- } +- + worldserver1.spawnEntityInWorld(entity); ++ // CraftBukkit start - Forward the CraftEntity to the new entity ++ this.getBukkitEntity().setHandle(entity); ++ entity.bukkitEntity = this.getBukkitEntity(); ++ // CraftBukkit end + } + + this.isDead = true; +@@ -2077,7 +2523,6 @@ + this.worldObj.theProfiler.endSection(); + } + } +- + public float func_145772_a(Explosion p_145772_1_, World p_145772_2_, int p_145772_3_, int p_145772_4_, int p_145772_5_, Block p_145772_6_) + { + return p_145772_6_.getExplosionResistance(this, p_145772_2_, p_145772_3_, p_145772_4_, p_145772_5_, posX, posY + getEyeHeight(), posZ); diff --git a/patches/net/minecraft/entity/EntityAgeable.java.patch b/patches/net/minecraft/entity/EntityAgeable.java.patch new file mode 100644 index 0000000..a620cac --- /dev/null +++ b/patches/net/minecraft/entity/EntityAgeable.java.patch @@ -0,0 +1,80 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityAgeable.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityAgeable.java +@@ -10,6 +10,36 @@ + { + private float field_98056_d = -1.0F; + private float field_98057_e; ++ public boolean ageLocked = false; // CraftBukkit ++ ++ // Spigot start ++ @Override ++ public void inactiveTick() ++ { ++ super.inactiveTick(); ++ ++ if (this.worldObj.isRemote || this.ageLocked) ++ { ++ // CraftBukkit ++ this.setScaleForAge(this.isChild()); ++ } ++ else ++ { ++ int i = this.getGrowingAge(); ++ ++ if (i < 0) ++ { ++ ++i; ++ this.setGrowingAge(i); ++ } ++ else if (i > 0) ++ { ++ --i; ++ this.setGrowingAge(i); ++ } ++ } ++ } ++ // Spigot end + private static final String __OBFID = "CL_00001530"; + + public EntityAgeable(World p_i1578_1_) +@@ -37,7 +67,7 @@ + { + entityageable.setGrowingAge(-24000); + entityageable.setLocationAndAngles(this.posX, this.posY, this.posZ, 0.0F, 0.0F); +- this.worldObj.spawnEntityInWorld(entityageable); ++ this.worldObj.addEntity(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // CraftBukkit + + if (itemstack.hasDisplayName()) + { +@@ -48,7 +78,7 @@ + { + --itemstack.stackSize; + +- if (itemstack.stackSize <= 0) ++ if (itemstack.stackSize == 0) // CraftBukkit - allow less than 0 stacks as "infinite" + { + p_70085_1_.inventory.setInventorySlotContents(p_70085_1_.inventory.currentItem, (ItemStack)null); + } +@@ -99,19 +129,21 @@ + { + super.writeEntityToNBT(p_70014_1_); + p_70014_1_.setInteger("Age", this.getGrowingAge()); ++ p_70014_1_.setBoolean("AgeLocked", this.ageLocked); // CraftBukkit + } + + public void readEntityFromNBT(NBTTagCompound p_70037_1_) + { + super.readEntityFromNBT(p_70037_1_); + this.setGrowingAge(p_70037_1_.getInteger("Age")); ++ this.ageLocked = p_70037_1_.getBoolean("AgeLocked"); // CraftBukkit + } + + public void onLivingUpdate() + { + super.onLivingUpdate(); + +- if (this.worldObj.isRemote) ++ if (this.worldObj.isRemote || this.ageLocked) // CraftBukkit + { + this.setScaleForAge(this.isChild()); + } diff --git a/patches/net/minecraft/entity/EntityCreature.java.patch b/patches/net/minecraft/entity/EntityCreature.java.patch new file mode 100644 index 0000000..d44004c --- /dev/null +++ b/patches/net/minecraft/entity/EntityCreature.java.patch @@ -0,0 +1,105 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityCreature.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityCreature.java +@@ -13,12 +13,18 @@ + import net.minecraft.util.Vec3; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.event.entity.EntityTargetEvent; ++import org.bukkit.event.entity.EntityUnleashEvent; ++// CraftBukkit end ++ + public abstract class EntityCreature extends EntityLiving + { + public static final UUID field_110179_h = UUID.fromString("E199AD21-BA8A-4C53-8D13-6182D5C69D3A"); + public static final AttributeModifier field_110181_i = (new AttributeModifier(field_110179_h, "Fleeing speed bonus", 2.0D, 2)).setSaved(false); +- private PathEntity pathToEntity; +- protected Entity entityToAttack; ++ public PathEntity pathToEntity; // CraftBukkit - private -> public ++ public Entity entityToAttack; // CraftBukkit - protected -> public + protected boolean hasAttacked; + protected int fleeingTick; + private ChunkCoordinates homePosition = new ChunkCoordinates(0, 0, 0); +@@ -52,8 +58,29 @@ + + if (this.entityToAttack == null) + { +- this.entityToAttack = this.findPlayerToAttack(); ++ // CraftBukkit start ++ Entity target = this.findPlayerToAttack(); + ++ if (target != null) ++ { ++ EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), target.getBukkitEntity(), EntityTargetEvent.TargetReason.CLOSEST_PLAYER); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ if (event.getTarget() == null) ++ { ++ this.entityToAttack = null; ++ } ++ else ++ { ++ this.entityToAttack = ((CraftEntity) event.getTarget()).getHandle(); ++ } ++ } ++ } ++ ++ // CraftBukkit end ++ + if (this.entityToAttack != null) + { + this.pathToEntity = this.worldObj.getPathEntityToEntity(this, this.entityToAttack, f4, true, false, false, true); +@@ -70,7 +97,23 @@ + } + else + { +- this.entityToAttack = null; ++ // CraftBukkit start ++ EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), null, EntityTargetEvent.TargetReason.TARGET_DIED); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ if (event.getTarget() == null) ++ { ++ this.entityToAttack = null; ++ } ++ else ++ { ++ this.entityToAttack = ((CraftEntity) event.getTarget()).getHandle(); ++ } ++ } ++ ++ // CraftBukkit end + } + + if (this.entityToAttack instanceof EntityPlayerMP && ((EntityPlayerMP)this.entityToAttack).theItemInWorldManager.isCreative()) +@@ -122,7 +165,8 @@ + double d1 = vec3.xCoord - this.posX; + double d2 = vec3.zCoord - this.posZ; + double d3 = vec3.yCoord - (double)i; +- float f1 = (float)(Math.atan2(d2, d1) * 180.0D / Math.PI) - 90.0F; ++ // CraftBukkit - Math -> TrigMath ++ float f1 = (float)(org.bukkit.craftbukkit.TrigMath.atan2(d2, d1) * 180.0D / Math.PI) - 90.0F; + float f2 = MathHelper.wrapAngleTo180_float(f1 - this.rotationYaw); + this.moveForward = (float)this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).getAttributeValue(); + +@@ -303,6 +347,7 @@ + { + if (f > 10.0F) + { ++ this.worldObj.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); // CraftBukkit + this.clearLeashed(true, true); + } + +@@ -335,6 +380,7 @@ + + if (f > 10.0F) + { ++ this.worldObj.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); // CraftBukkit + this.clearLeashed(true, true); + } + } diff --git a/patches/net/minecraft/entity/EntityHanging.java.patch b/patches/net/minecraft/entity/EntityHanging.java.patch new file mode 100644 index 0000000..7e5ae4e --- /dev/null +++ b/patches/net/minecraft/entity/EntityHanging.java.patch @@ -0,0 +1,119 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityHanging.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityHanging.java +@@ -10,6 +10,14 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++// CraftBukkit start ++import net.minecraft.entity.item.EntityPainting; ++import org.bukkit.entity.Hanging; ++import org.bukkit.entity.Painting; ++import org.bukkit.event.hanging.HangingBreakEvent; ++import org.bukkit.event.painting.PaintingBreakEvent; ++// CraftBukkit end ++ + public abstract class EntityHanging extends Entity + { + private int tickCounter1; +@@ -125,6 +133,38 @@ + + if (!this.isDead && !this.onValidSurface()) + { ++ // CraftBukkit start ++ Material material = this.worldObj.getBlock((int) this.posX, (int) this.posY, (int) this.posZ).getMaterial(); ++ HangingBreakEvent.RemoveCause cause; ++ ++ if (!material.equals(Material.air)) ++ { ++ // TODO: This feels insufficient to catch 100% of suffocation cases ++ cause = HangingBreakEvent.RemoveCause.OBSTRUCTION; ++ } ++ else ++ { ++ cause = HangingBreakEvent.RemoveCause.PHYSICS; ++ } ++ ++ HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), cause); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ PaintingBreakEvent paintingEvent = null; ++ ++ if (this instanceof EntityPainting) ++ { ++ // Fire old painting event until it can be removed ++ paintingEvent = new PaintingBreakEvent((Painting) this.getBukkitEntity(), PaintingBreakEvent.RemoveCause.valueOf(cause.name())); ++ paintingEvent.setCancelled(event.isCancelled()); ++ this.worldObj.getServer().getPluginManager().callEvent(paintingEvent); ++ } ++ ++ if (isDead || event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.setDead(); + this.onBroken((Entity)null); + } +@@ -233,6 +273,39 @@ + { + if (!this.isDead && !this.worldObj.isRemote) + { ++ // CraftBukkit start ++ HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.DEFAULT); ++ PaintingBreakEvent paintingEvent = null; ++ ++ if (p_70097_1_.getEntity() != null) ++ { ++ event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((Hanging) this.getBukkitEntity(), p_70097_1_.getEntity() == null ? null : p_70097_1_.getEntity().getBukkitEntity()); ++ ++ if (this instanceof EntityPainting) ++ { ++ // Fire old painting event until it can be removed ++ paintingEvent = new org.bukkit.event.painting.PaintingBreakByEntityEvent((Painting) this.getBukkitEntity(), p_70097_1_.getEntity() == null ? null : p_70097_1_.getEntity().getBukkitEntity()); ++ } ++ } ++ else if (p_70097_1_.isExplosion()) ++ { ++ event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.EXPLOSION); ++ } ++ ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (paintingEvent != null) ++ { ++ paintingEvent.setCancelled(event.isCancelled()); ++ this.worldObj.getServer().getPluginManager().callEvent(paintingEvent); ++ } ++ ++ if (this.isDead || event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) ++ { ++ return true; ++ } ++ ++ // CraftBukkit end + this.setDead(); + this.setBeenAttacked(); + this.onBroken(p_70097_1_.getEntity()); +@@ -246,6 +319,22 @@ + { + if (!this.worldObj.isRemote && !this.isDead && p_70091_1_ * p_70091_1_ + p_70091_3_ * p_70091_3_ + p_70091_5_ * p_70091_5_ > 0.0D) + { ++ if (this.isDead) ++ { ++ return; // CraftBukkit ++ } ++ ++ // CraftBukkit start ++ // TODO - Does this need its own cause? Seems to only be triggered by pistons ++ HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.PHYSICS); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (this.isDead || event.isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.setDead(); + this.onBroken((Entity)null); + } diff --git a/patches/net/minecraft/entity/EntityLeashKnot.java.patch b/patches/net/minecraft/entity/EntityLeashKnot.java.patch new file mode 100644 index 0000000..bcab608 --- /dev/null +++ b/patches/net/minecraft/entity/EntityLeashKnot.java.patch @@ -0,0 +1,74 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityLeashKnot.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityLeashKnot.java +@@ -11,6 +11,12 @@ + import net.minecraft.util.AxisAlignedBB; + import net.minecraft.world.World; + ++ // CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.network.play.server.S1BPacketEntityAttach; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++// CraftBukkit end ++ + public class EntityLeashKnot extends EntityHanging + { + private static final String __OBFID = "CL_00001548"; +@@ -84,6 +90,14 @@ + + if (entityliving.getLeashed() && entityliving.getLeashedToEntity() == p_130002_1_) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callPlayerLeashEntityEvent(entityliving, this, p_130002_1_).isCancelled()) ++ { ++ ((EntityPlayerMP) p_130002_1_).playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(1, entityliving, entityliving.getLeashedToEntity())); ++ continue; ++ } ++ ++ // CraftBukkit end + entityliving.setLeashedToEntity(this, true); + flag = true; + } +@@ -93,9 +107,12 @@ + + if (!this.worldObj.isRemote && !flag) + { +- this.setDead(); ++ // CraftBukkit start - Move below ++ //this.setDead(); ++ boolean die = true; + +- if (p_130002_1_.capabilities.isCreativeMode) ++ // CraftBukkit end ++ if (true || p_130002_1_.capabilities.isCreativeMode) // CraftBukkit - Process for non-creative as well + { + d0 = 7.0D; + list = this.worldObj.getEntitiesWithinAABB(EntityLiving.class, AxisAlignedBB.getBoundingBox(this.posX - d0, this.posY - d0, this.posZ - d0, this.posX + d0, this.posY + d0, this.posZ + d0)); +@@ -110,11 +127,27 @@ + + if (entityliving.getLeashed() && entityliving.getLeashedToEntity() == this) + { +- entityliving.clearLeashed(true, false); ++ // CraftBukkit start ++ if (CraftEventFactory.callPlayerUnleashEntityEvent(entityliving, p_130002_1_).isCancelled()) ++ { ++ die = false; ++ continue; ++ } ++ ++ entityliving.clearLeashed(true, !p_130002_1_.capabilities.isCreativeMode); // false -> survival mode boolean ++ // CraftBukkit end + } + } + } + } ++ ++ // CraftBukkit start ++ if (die) ++ { ++ this.setDead(); ++ } ++ ++ // CraftBukkit end + } + + return true; diff --git a/patches/net/minecraft/entity/EntityLiving.java.patch b/patches/net/minecraft/entity/EntityLiving.java.patch new file mode 100644 index 0000000..ce5ad09 --- /dev/null +++ b/patches/net/minecraft/entity/EntityLiving.java.patch @@ -0,0 +1,162 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityLiving.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityLiving.java +@@ -38,6 +38,13 @@ + import cpw.mods.fml.common.eventhandler.Event.Result; + import net.minecraftforge.event.ForgeEventFactory; + ++// CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityUnleashEvent; ++import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason; ++// CraftBukkit end ++ + public abstract class EntityLiving extends EntityLivingBase + { + public int livingSoundTime; +@@ -52,9 +59,9 @@ + private EntityLivingBase attackTarget; + private EntitySenses senses; + private ItemStack[] equipment = new ItemStack[5]; +- protected float[] equipmentDropChances = new float[5]; +- private boolean canPickUpLoot; +- private boolean persistenceRequired; ++ public float[] equipmentDropChances = new float[5]; // CraftBukkit - protected -> public ++ public boolean canPickUpLoot; // CraftBukkit - private -> public ++ public boolean persistenceRequired; // CraftBukkit - private -> public + protected float defaultPitch; + private Entity currentTarget; + protected int numTicksToChaseTarget; +@@ -311,9 +318,23 @@ + public void readEntityFromNBT(NBTTagCompound p_70037_1_) + { + super.readEntityFromNBT(p_70037_1_); +- this.setCanPickUpLoot(p_70037_1_.getBoolean("CanPickUpLoot")); +- this.persistenceRequired = p_70037_1_.getBoolean("PersistenceRequired"); ++ // CraftBukkit start - If looting or persistence is false only use it if it was set after we started using it ++ boolean data = p_70037_1_.getBoolean("CanPickUpLoot"); + ++ if (isLevelAtLeast(p_70037_1_, 1) || data) ++ { ++ this.canPickUpLoot = data; ++ } ++ ++ data = p_70037_1_.getBoolean("PersistenceRequired"); ++ ++ if (isLevelAtLeast(p_70037_1_, 1) || data) ++ { ++ this.persistenceRequired = data; ++ } ++ ++ // CraftBukkit end ++ + if (p_70037_1_.hasKey("CustomName", 8) && p_70037_1_.getString("CustomName").length() > 0) + { + this.setCustomNameTag(p_70037_1_.getString("CustomName")); +@@ -521,15 +542,36 @@ + this.entityAge = 0; + } + } ++ // Cauldron start - Force despawn of entity if a player isn't near ++ else if (this.worldObj.cauldronConfig.entityDespawnImmediate && this.canDespawn()) ++ { ++ this.despawn("No Player : Immediate"); ++ } ++ // Cauldron end + } + } + ++ // Cauldron start ++ private void despawn(String reason) { ++ this.setDead(); ++ net.minecraftforge.cauldron.CauldronHooks.logEntityDespawn(this, reason); ++ } ++ // Cauldron end ++ + protected void updateAITasks() + { + ++this.entityAge; + this.worldObj.theProfiler.startSection("checkDespawn"); + this.despawnEntity(); + this.worldObj.theProfiler.endSection(); ++ ++ // Spigot Start ++ if (this.fromMobSpawner) ++ { ++ return; ++ } ++ ++ // Spigot End + this.worldObj.theProfiler.startSection("sensing"); + this.senses.clearSensingCache(); + this.worldObj.theProfiler.endSection(); +@@ -1005,6 +1047,14 @@ + { + if (this.getLeashed() && this.getLeashedToEntity() == p_130002_1_) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callPlayerUnleashEntityEvent(this, p_130002_1_).isCancelled()) ++ { ++ ((EntityPlayerMP) p_130002_1_).playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(1, this, this.getLeashedToEntity())); ++ return false; ++ } ++ ++ // CraftBukkit end + this.clearLeashed(true, !p_130002_1_.capabilities.isCreativeMode); + return true; + } +@@ -1016,6 +1066,14 @@ + { + if (!(this instanceof EntityTameable) || !((EntityTameable)this).isTamed()) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callPlayerLeashEntityEvent(this, p_130002_1_, p_130002_1_).isCancelled()) ++ { ++ ((EntityPlayerMP) p_130002_1_).playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(1, this, this.getLeashedToEntity())); ++ return false; ++ } ++ ++ // CraftBukkit end + this.setLeashedToEntity(p_130002_1_, true); + --itemstack.stackSize; + return true; +@@ -1023,6 +1081,14 @@ + + if (((EntityTameable)this).func_152114_e(p_130002_1_)) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callPlayerLeashEntityEvent(this, p_130002_1_, p_130002_1_).isCancelled()) ++ { ++ ((EntityPlayerMP) p_130002_1_).playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(1, this, this.getLeashedToEntity())); ++ return false; ++ } ++ ++ // CraftBukkit end + this.setLeashedToEntity(p_130002_1_, true); + --itemstack.stackSize; + return true; +@@ -1049,6 +1115,7 @@ + { + if (this.leashedToEntity == null || this.leashedToEntity.isDead) + { ++ this.worldObj.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.HOLDER_GONE)); // CraftBukkit + this.clearLeashed(true, true); + } + } +@@ -1136,10 +1203,16 @@ + } + else + { ++ this.worldObj.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN)); // CraftBukkit + this.clearLeashed(false, true); + } + } + + this.field_110170_bx = null; + } ++ ++ public boolean canDespawn_CB() ++ { ++ return this.canDespawn(); ++ } + } diff --git a/patches/net/minecraft/entity/EntityLivingBase.java.patch b/patches/net/minecraft/entity/EntityLivingBase.java.patch new file mode 100644 index 0000000..c4f3d54 --- /dev/null +++ b/patches/net/minecraft/entity/EntityLivingBase.java.patch @@ -0,0 +1,657 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityLivingBase.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityLivingBase.java +@@ -50,14 +50,28 @@ + import net.minecraft.world.World; + import net.minecraft.world.WorldServer; + import net.minecraftforge.common.ForgeHooks; ++import net.minecraftforge.common.ISpecialArmor.ArmorProperties; + ++// CraftBukkit start ++import net.minecraft.nbt.NBTTagInt; ++import net.minecraft.network.play.server.S28PacketEffect; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityDamageEvent; ++import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; ++import org.bukkit.event.entity.EntityRegainHealthEvent; ++// CraftBukkit end ++import org.bukkit.craftbukkit.SpigotTimings; // Spigot ++import org.bukkit.craftbukkit.inventory.CraftItemStack; // Cauldron ++ ++import com.google.common.base.Function; ++ + public abstract class EntityLivingBase extends Entity + { + private static final UUID sprintingSpeedBoostModifierUUID = UUID.fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); + private static final AttributeModifier sprintingSpeedBoostModifier = (new AttributeModifier(sprintingSpeedBoostModifierUUID, "Sprinting speed boost", 0.30000001192092896D, 2)).setSaved(false); + private BaseAttributeMap attributeMap; +- private final CombatTracker _combatTracker = new CombatTracker(this); +- private final HashMap activePotionsMap = new HashMap(); ++ public CombatTracker _combatTracker = new CombatTracker(this); // CraftBukkit - private -> public, remove final ++ public final HashMap activePotionsMap = new HashMap(); // CraftBukkit - protected -> public + private final ItemStack[] previousEquipment = new ItemStack[5]; + public boolean isSwingInProgress; + public int swingProgressInt; +@@ -83,7 +97,7 @@ + public float rotationYawHead; + public float prevRotationYawHead; + public float jumpMovementFactor = 0.02F; +- protected EntityPlayer attackingPlayer; ++ public EntityPlayer attackingPlayer; // CraftBukkit - protected -> public + protected int recentlyHit; + protected boolean dead; + protected int entityAge; +@@ -93,7 +107,7 @@ + protected float field_70763_ax; + protected float field_70741_aB; + protected int scoreValue; +- protected float lastDamage; ++ public float lastDamage; // CraftBukkit - protected -> public + protected boolean isJumping; + public float moveStrafing; + public float moveForward; +@@ -104,21 +118,26 @@ + protected double newPosZ; + protected double newRotationYaw; + protected double newRotationPitch; +- private boolean potionsNeedUpdate = true; +- private EntityLivingBase entityLivingToAttack; ++ public boolean potionsNeedUpdate = true; // CraftBukkit - private -> public ++ public EntityLivingBase entityLivingToAttack; // CraftBukkit - private -> public + private int revengeTimer; + private EntityLivingBase lastAttacker; + private int lastAttackerTime; + private float landMovementFactor; + private int jumpTicks; + private float field_110151_bq; ++ // CraftBukkit start ++ public int expToDrop; ++ public int maxAirTicks = 300; ++ // CraftBukkit end + private static final String __OBFID = "CL_00001549"; + + public EntityLivingBase(World p_i1594_1_) + { + super(p_i1594_1_); + this.applyEntityAttributes(); +- this.setHealth(this.getMaxHealth()); ++ // CraftBukkit - this.setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor ++ this.dataWatcher.updateObject(6, (float) this.getEntityAttribute(SharedMonsterAttributes.maxHealth).getAttributeValue()); + this.preventEntitySpawning = true; + this.field_70770_ap = (float)(Math.random() + 1.0D) * 0.01F; + this.setPosition(this.posX, this.posY, this.posZ); +@@ -173,7 +192,18 @@ + } + else if (!this.worldObj.isRemote && this.fallDistance > 3.0F) + { +- this.worldObj.playAuxSFX(2006, i, j, k, MathHelper.ceiling_float_int(this.fallDistance - 3.0F)); ++ // CraftBukkit start - supply player as argument in particles for visibility API to work ++ if (this instanceof EntityPlayerMP) ++ { ++ this.worldObj.playAuxSFXAtEntity((EntityPlayer) this, 2006, i, j, k, MathHelper.ceiling_float_int(this.fallDistance - 3.0F)); ++ ((EntityPlayerMP) this).playerNetServerHandler.sendPacket(new S28PacketEffect(2006, i, j, k, MathHelper ++ .ceiling_float_int(this.fallDistance - 3.0F), false)); ++ } ++ else ++ { ++ this.worldObj.playAuxSFX(2006, i, j, k, MathHelper.ceiling_float_int(this.fallDistance - 3.0F)); ++ } ++ // CraftBukkit end + } + + block.onFallenUpon(this.worldObj, i, j, k, this, this.fallDistance); +@@ -234,7 +264,12 @@ + } + else + { +- this.setAir(300); ++ // CraftBukkit start - Only set if needed to work around a DataWatcher inefficiency ++ if (this.getAir() != 300) ++ { ++ this.setAir(maxAirTicks); ++ } ++ // CraftBukkit end + } + + if (this.isEntityAlive() && this.isWet()) +@@ -299,6 +334,22 @@ + this.worldObj.theProfiler.endSection(); + } + ++ // CraftBukkit start ++ public int getExpReward() ++ { ++ int exp = this.getExperiencePoints(this.attackingPlayer); ++ ++ if (!this.worldObj.isRemote && (this.recentlyHit > 0 || this.isPlayer()) && this.func_146066_aG()) ++ { ++ return exp; ++ } ++ else ++ { ++ return 0; ++ } ++ } ++ // CraftBukkit end ++ + public boolean isChild() + { + return false; +@@ -308,22 +359,21 @@ + { + ++this.deathTime; + +- if (this.deathTime == 20) ++ if (this.deathTime >= 20 && !this.isDead) // CraftBukkit - (this.deathTicks == 20) -> (this.deathTicks >= 20 && !this.dead) + { + int i; ++ // CraftBukkit start - Update getExpReward() above if the removed if() changes! ++ i = this.expToDrop; + +- if (!this.worldObj.isRemote && (this.recentlyHit > 0 || this.isPlayer()) && this.func_146066_aG() && this.worldObj.getGameRules().getGameRuleBooleanValue("doMobLoot")) ++ while (i > 0) + { +- i = this.getExperiencePoints(this.attackingPlayer); +- +- while (i > 0) +- { +- int j = EntityXPOrb.getXPSplit(i); +- i -= j; +- this.worldObj.spawnEntityInWorld(new EntityXPOrb(this.worldObj, this.posX, this.posY, this.posZ, j)); +- } ++ int j = EntityXPOrb.getXPSplit(i); ++ i -= j; ++ this.worldObj.spawnEntityInWorld(new EntityXPOrb(this.worldObj, this.posX, this.posY, this.posZ, j)); + } + ++ this.expToDrop = 0; ++ // CraftBukkit end + this.setDead(); + + for (i = 0; i < 20; ++i) +@@ -485,6 +535,22 @@ + } + } + ++ // CraftBukkit start ++ if (p_70037_1_.hasKey("Bukkit.MaxHealth")) ++ { ++ NBTBase nbtbase = p_70037_1_.getTag("Bukkit.MaxHealth"); ++ ++ if (nbtbase.getId() == 5) ++ { ++ this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue((double) ((NBTTagFloat) nbtbase).func_150291_c()); ++ } ++ else if (nbtbase.getId() == 3) ++ { ++ this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue((double) ((NBTTagInt) nbtbase).func_150287_d()); ++ } ++ } ++ // CraftBukkit end ++ + if (p_70037_1_.hasKey("HealF", 99)) + { + this.setHealth(p_70037_1_.getFloat("HealF")); +@@ -614,12 +680,14 @@ + + public boolean isPotionActive(int p_82165_1_) + { +- return this.activePotionsMap.containsKey(Integer.valueOf(p_82165_1_)); ++ // CraftBukkit - Add size check for efficiency ++ return this.activePotionsMap.size() != 0 && this.activePotionsMap.containsKey(Integer.valueOf(p_82165_1_)); + } + + public boolean isPotionActive(Potion p_70644_1_) + { +- return this.activePotionsMap.containsKey(Integer.valueOf(p_70644_1_.id)); ++ // CraftBukkit - Add size check for efficiency ++ return this.activePotionsMap.size() != 0 && this.activePotionsMap.containsKey(Integer.valueOf(p_70644_1_.id)); + } + + public PotionEffect getActivePotionEffect(Potion p_70660_1_) +@@ -710,25 +778,66 @@ + } + } + ++ // CraftBukkit start - Delegate so we can handle providing a reason for health being regained + public void heal(float p_70691_1_) + { ++ heal(p_70691_1_, EntityRegainHealthEvent.RegainReason.CUSTOM); ++ } ++ ++ public void heal(float p_70691_1_, EntityRegainHealthEvent.RegainReason regainReason) ++ { + p_70691_1_ = net.minecraftforge.event.ForgeEventFactory.onLivingHeal(this, p_70691_1_); + if (p_70691_1_ <= 0) return; + float f1 = this.getHealth(); + + if (f1 > 0.0F) + { +- this.setHealth(f1 + p_70691_1_); ++ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), p_70691_1_, regainReason); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.setHealth((float) (this.getHealth() + event.getAmount())); ++ } + } + } + + public final float getHealth() + { ++ // CraftBukkit start - Use unscaled health ++ if (this instanceof EntityPlayerMP) ++ { ++ return (float) ((EntityPlayerMP) this).getBukkitEntity().getHealth(); ++ } ++ // CraftBukkit end + return this.dataWatcher.getWatchableObjectFloat(6); + } + + public void setHealth(float p_70606_1_) + { ++ // CraftBukkit start - Handle scaled health ++ if (this instanceof EntityPlayerMP) ++ { ++ org.bukkit.craftbukkit.entity.CraftPlayer player = ((EntityPlayerMP) this).getBukkitEntity(); ++ ++ // Squeeze ++ if (p_70606_1_ < 0.0F) ++ { ++ player.setRealHealth(0.0D); ++ } ++ else if (p_70606_1_ > player.getMaxHealth()) ++ { ++ player.setRealHealth(player.getMaxHealth()); ++ } ++ else ++ { ++ player.setRealHealth(p_70606_1_); ++ } ++ ++ this.dataWatcher.updateObject(6, Float.valueOf(player.getScaledHealth())); ++ return; ++ } ++ // CraftBukkit end + this.dataWatcher.updateObject(6, Float.valueOf(MathHelper.clamp_float(p_70606_1_, 0.0F, this.getMaxHealth()))); + } + +@@ -757,7 +866,8 @@ + } + else + { +- if ((p_70097_1_ == DamageSource.anvil || p_70097_1_ == DamageSource.fallingBlock) && this.getEquipmentInSlot(4) != null) ++ // CraftBukkit - Moved into damageEntity_CB(DamageSource, float) ++ if (false && (p_70097_1_ == DamageSource.anvil || p_70097_1_ == DamageSource.fallingBlock) && this.getEquipmentInSlot(4) != null) + { + this.getEquipmentInSlot(4).damageItem((int)(p_70097_2_ * 4.0F + this.rand.nextFloat() * p_70097_2_ * 2.0F), this); + p_70097_2_ *= 0.75F; +@@ -773,16 +883,27 @@ + return false; + } + +- this.damageEntity(p_70097_1_, p_70097_2_ - this.lastDamage); ++ // CraftBukkit start ++ if (!this.damageEntity_CB(p_70097_1_, p_70097_2_ - this.lastDamage)) ++ { ++ return false; ++ } ++ // CraftBukkit end + this.lastDamage = p_70097_2_; + flag = false; + } + else + { ++ // CraftBukkit start ++ float previousHealth = this.getHealth(); ++ if (!this.damageEntity_CB(p_70097_1_, p_70097_2_)) ++ { ++ return false; ++ } + this.lastDamage = p_70097_2_; +- this.prevHealth = this.getHealth(); ++ this.prevHealth = previousHealth; + this.hurtResistantTime = this.maxHurtResistantTime; +- this.damageEntity(p_70097_1_, p_70097_2_); ++ // CraftBukkit end + this.hurtTime = this.maxHurtTime = 10; + } + +@@ -938,6 +1059,22 @@ + + if (!ForgeHooks.onLivingDrops(this, p_70645_1_, capturedDrops, i, recentlyHit > 0, j)) + { ++ // Cauldron start - capture drops for plugins then fire event ++ if (this.capturedDrops.size() > 0) ++ { ++ java.util.List loot = new java.util.ArrayList(); ++ for (EntityItem item : capturedDrops) ++ { ++ loot.add(CraftItemStack.asCraftMirror(item.getEntityItem())); ++ } ++ CraftEventFactory.callEntityDeathEvent(this, loot); ++ } ++ else ++ { ++ CraftEventFactory.callEntityDeathEvent(this); ++ } ++ // Cauldron end ++ + for (EntityItem item : capturedDrops) + { + worldObj.spawnEntityInWorld(item); +@@ -1010,8 +1147,17 @@ + + if (i > 0) + { ++ // CraftBukkit start ++ if (!this.attackEntityFrom(DamageSource.fall, (float) i)) ++ { ++ return; ++ } ++ } ++ // CraftBukkit end ++ if (i > 0) ++ { + this.playSound(this.func_146067_o(i), 1.0F, 1.0F); +- this.attackEntityFrom(DamageSource.fall, (float)i); ++ // this.attackEntityFrom(DamageSource.fall, (float)i); // CraftBukkit - moved up + int j = MathHelper.floor_double(this.posX); + int k = MathHelper.floor_double(this.posY - 0.20000000298023224D - (double)this.yOffset); + int l = MathHelper.floor_double(this.posZ); +@@ -1065,7 +1211,7 @@ + { + int i = 25 - this.getTotalArmorValue(); + float f1 = p_70655_2_ * (float)i; +- this.damageArmor(p_70655_2_); ++ // this.damageArmor(p_70655_2_); // CraftBukkit - Moved into damageEntity_CB(DamageSource, float) + p_70655_2_ = f1 / 25.0F; + } + +@@ -1089,7 +1235,8 @@ + int j; + float f1; + +- if (this.isPotionActive(Potion.resistance) && p_70672_1_ != DamageSource.outOfWorld) ++ // CraftBukkit - Moved to damageEntity_CB(DamageSource, float) ++ if (false && this.isPotionActive(Potion.resistance) && p_70672_1_ != DamageSource.outOfWorld) + { + i = (this.getActivePotionEffect(Potion.resistance).getAmplifier() + 1) * 5; + j = 25 - i; +@@ -1122,26 +1269,156 @@ + } + } + ++ // Cauldron start - vanilla compatibility + protected void damageEntity(DamageSource p_70665_1_, float p_70665_2_) + { ++ this.damageEntity_CB(p_70665_1_, p_70665_2_); ++ } ++ ++ // Cauldron end ++ ++ // CraftBukkit start ++ protected boolean damageEntity_CB(final DamageSource damagesource, float f) ++ { // void -> boolean, add final + if (!this.isEntityInvulnerable()) + { +- p_70665_2_ = ForgeHooks.onLivingHurt(this, p_70665_1_, p_70665_2_); +- if (p_70665_2_ <= 0) return; +- p_70665_2_ = this.applyArmorCalculations(p_70665_1_, p_70665_2_); +- p_70665_2_ = this.applyPotionDamageCalculations(p_70665_1_, p_70665_2_); +- float f1 = p_70665_2_; +- p_70665_2_ = Math.max(p_70665_2_ - this.getAbsorptionAmount(), 0.0F); +- this.setAbsorptionAmount(this.getAbsorptionAmount() - (f1 - p_70665_2_)); ++ final boolean human = this instanceof EntityPlayer; ++ float originalDamage = f; ++ // Cauldron start - apply forge damage hook ++ f = ForgeHooks.onLivingHurt(this, damagesource, f); ++ if (f <= 0) return false; ++ // Cauldron end ++ Function hardHat = new Function() { ++ @Override ++ public Double apply(Double f) ++ { ++ if ((damagesource == DamageSource.anvil || damagesource == DamageSource.fallingBlock) ++ && EntityLivingBase.this.getEquipmentInSlot(4) != null) ++ { ++ return -(f - (f * 0.75F)); ++ } ++ return -0.0; ++ } ++ }; + +- if (p_70665_2_ != 0.0F) ++ float hardHatModifier = hardHat.apply((double) f).floatValue(); ++ f += hardHatModifier; ++ ++ Function blocking = new Function() { ++ @Override ++ public Double apply(Double f) ++ { ++ if (human) ++ { ++ if (!damagesource.isUnblockable() && ((EntityPlayer) EntityLivingBase.this).isBlocking() && f > 0.0F) ++ { ++ return -(f - ((1.0F + f) * 0.5F)); ++ } ++ } ++ return -0.0; ++ } ++ }; ++ float blockingModifier = blocking.apply((double) f).floatValue(); ++ f += blockingModifier; ++ ++ Function armor = new Function() { ++ @Override ++ public Double apply(Double f) ++ { ++ // Cauldron start - apply forge armor hook ++ if (human) ++ { ++ return -(f - ArmorProperties.ApplyArmor(EntityLivingBase.this, ((EntityPlayer) EntityLivingBase.this).inventory.armorInventory, ++ damagesource, f.floatValue())); ++ } ++ // Cauldron end ++ return -(f - EntityLivingBase.this.applyArmorCalculations(damagesource, f.floatValue())); ++ } ++ }; ++ float armorModifier = armor.apply((double) f).floatValue(); ++ f += armorModifier; ++ ++ Function resistance = new Function() { ++ @Override ++ public Double apply(Double f) ++ { ++ if (!damagesource.isDamageAbsolute() && EntityLivingBase.this.isPotionActive(Potion.resistance) && damagesource != DamageSource.outOfWorld) ++ { ++ int i = (EntityLivingBase.this.getActivePotionEffect(Potion.resistance).getAmplifier() + 1) * 5; ++ int j = 25 - i; ++ float f1 = f.floatValue() * (float) j; ++ return -(f - (f1 / 25.0F)); ++ } ++ return -0.0; ++ } ++ }; ++ float resistanceModifier = resistance.apply((double) f).floatValue(); ++ f += resistanceModifier; ++ ++ Function magic = new Function() { ++ @Override ++ public Double apply(Double f) ++ { ++ return -(f - EntityLivingBase.this.applyPotionDamageCalculations(damagesource, f.floatValue())); ++ } ++ }; ++ float magicModifier = magic.apply((double) f).floatValue(); ++ f += magicModifier; ++ ++ Function absorption = new Function() { ++ @Override ++ public Double apply(Double f) ++ { ++ return -(Math.max(f - Math.max(f - EntityLivingBase.this.getAbsorptionAmount(), 0.0F), 0.0F)); ++ } ++ }; ++ float absorptionModifier = absorption.apply((double) f).floatValue(); ++ ++ EntityDamageEvent event = CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, hardHatModifier, blockingModifier, ++ armorModifier, resistanceModifier, magicModifier, absorptionModifier, hardHat, blocking, armor, resistance, magic, absorption); ++ if (event.isCancelled()) + { ++ return false; ++ } ++ ++ f = (float) event.getFinalDamage(); ++ ++ // Apply damage to helmet ++ if ((damagesource == DamageSource.anvil || damagesource == DamageSource.fallingBlock) && this.getEquipmentInSlot(4) != null) ++ { ++ this.getEquipmentInSlot(4).damageItem((int) (event.getDamage() * 4.0F + this.rand.nextFloat() * event.getDamage() * 2.0F), this); ++ } ++ ++ // Apply damage to armor ++ if (!damagesource.isUnblockable()) ++ { ++ float armorDamage = (float) (event.getDamage() + event.getDamage(DamageModifier.BLOCKING) + event.getDamage(DamageModifier.HARD_HAT)); ++ this.damageArmor(armorDamage); ++ } ++ ++ absorptionModifier = (float) -event.getDamage(DamageModifier.ABSORPTION); ++ this.setAbsorptionAmount(Math.max(this.getAbsorptionAmount() - absorptionModifier, 0.0F)); ++ if (f != 0.0F) ++ { ++ if (human) ++ { ++ ((EntityPlayer) this).addExhaustion(damagesource.getHungerDamage()); ++ } ++ // CraftBukkit end + float f2 = this.getHealth(); +- this.setHealth(f2 - p_70665_2_); +- this.func_110142_aN().func_94547_a(p_70665_1_, f2, p_70665_2_); +- this.setAbsorptionAmount(this.getAbsorptionAmount() - p_70665_2_); ++ this.setHealth(f2 - f); ++ this.func_110142_aN().func_94547_a(damagesource, f2, f); ++ // CraftBukkit start ++ if (human) ++ { ++ return true; ++ } ++ // CraftBukkit end ++ this.setAbsorptionAmount(this.getAbsorptionAmount() - f); + } ++ return true; // CraftBukkit + } ++ return false; // CraftBukkit + } + + public CombatTracker func_110142_aN() +@@ -1558,6 +1835,7 @@ + public void onUpdate() + { + if (ForgeHooks.onLivingUpdate(this)) return; ++ SpigotTimings.timerEntityBaseTick.startTiming(); // Spigot + super.onUpdate(); + + if (!this.worldObj.isRemote) +@@ -1608,7 +1886,9 @@ + } + } + ++ SpigotTimings.timerEntityBaseTick.stopTiming(); // Spigot + this.onLivingUpdate(); ++ SpigotTimings.timerEntityTickRest.startTiming(); // Spigot + double d0 = this.posX - this.prevPosX; + double d1 = this.posZ - this.prevPosZ; + float f = (float)(d0 * d0 + d1 * d1); +@@ -1621,7 +1901,8 @@ + { + f3 = 1.0F; + f2 = (float)Math.sqrt((double)f) * 3.0F; +- f1 = (float)Math.atan2(d1, d0) * 180.0F / (float)Math.PI - 90.0F; ++ // CraftBukkit - Math -> TrigMath ++ f1 = (float) org.bukkit.craftbukkit.TrigMath.atan2(d1, d0) * 180.0F / (float)Math.PI - 90.0F; + } + + if (this.swingProgress > 0.0F) +@@ -1682,6 +1963,7 @@ + + this.worldObj.theProfiler.endSection(); + this.field_70764_aw += f2; ++ SpigotTimings.timerEntityTickRest.stopTiming(); // Spigot + } + + protected float func_110146_f(float p_110146_1_, float p_110146_2_) +@@ -1757,6 +2039,7 @@ + this.motionZ = 0.0D; + } + ++ SpigotTimings.timerEntityAI.startTiming(); // Spigot + this.worldObj.theProfiler.startSection("ai"); + + if (this.isMovementBlocked()) +@@ -1783,6 +2066,7 @@ + } + } + ++ SpigotTimings.timerEntityAI.stopTiming(); // Spigot + this.worldObj.theProfiler.endSection(); + this.worldObj.theProfiler.startSection("jump"); + +@@ -1811,13 +2095,17 @@ + this.moveStrafing *= 0.98F; + this.moveForward *= 0.98F; + this.randomYawVelocity *= 0.9F; ++ SpigotTimings.timerEntityAIMove.startTiming(); // Spigot + this.moveEntityWithHeading(this.moveStrafing, this.moveForward); ++ SpigotTimings.timerEntityAIMove.stopTiming(); // Spigot + this.worldObj.theProfiler.endSection(); + this.worldObj.theProfiler.startSection("push"); + + if (!this.worldObj.isRemote) + { ++ SpigotTimings.timerEntityAICollision.startTiming(); // Spigot + this.collideWithNearbyEntities(); ++ SpigotTimings.timerEntityAICollision.stopTiming(); // Spigot + } + + this.worldObj.theProfiler.endSection(); +@@ -1829,17 +2117,36 @@ + { + List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.expand(0.20000000298023224D, 0.0D, 0.20000000298023224D)); + +- if (list != null && !list.isEmpty()) ++ if (this.canBeCollidedWith() && list != null && !list.isEmpty()) // Spigot: Add this.canBeCollidedWith() condition + { ++ numCollisions -= worldObj.getSpigotConfig().maxCollisionsPerEntity; // Spigot // Cauldron ++ + for (int i = 0; i < list.size(); ++i) + { +- Entity entity = (Entity)list.get(i); ++ if (numCollisions > worldObj.getSpigotConfig().maxCollisionsPerEntity) // Cauldron ++ { ++ break; // Spigot ++ } + ++ Entity entity = (Entity) list.get(i); ++ ++ // TODO better check now? ++ // CraftBukkit start - Only handle mob (non-player) collisions ++ // every other tick ++ if (entity instanceof EntityLivingBase && !(this instanceof EntityPlayerMP) && this.ticksExisted % 2 == 0) ++ { ++ continue; ++ } ++ // CraftBukkit end ++ + if (entity.canBePushed()) + { ++ entity.numCollisions++; // Spigot ++ numCollisions++; // Spigot + this.collideWithEntity(entity); + } + } ++ numCollisions = 0; // Spigot + } + } + diff --git a/patches/net/minecraft/entity/EntityMinecartCommandBlock.java.patch b/patches/net/minecraft/entity/EntityMinecartCommandBlock.java.patch new file mode 100644 index 0000000..af78d15 --- /dev/null +++ b/patches/net/minecraft/entity/EntityMinecartCommandBlock.java.patch @@ -0,0 +1,39 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityMinecartCommandBlock.java +@@ -16,33 +16,9 @@ + + public class EntityMinecartCommandBlock extends EntityMinecart + { +- private final CommandBlockLogic field_145824_a = new CommandBlockLogic() +- { +- private static final String __OBFID = "CL_00001673"; +- public void func_145756_e() +- { +- EntityMinecartCommandBlock.this.getDataWatcher().updateObject(23, this.func_145753_i()); +- EntityMinecartCommandBlock.this.getDataWatcher().updateObject(24, IChatComponent.Serializer.func_150696_a(this.func_145749_h())); +- } +- @SideOnly(Side.CLIENT) +- public int func_145751_f() +- { +- return 1; +- } +- @SideOnly(Side.CLIENT) +- public void func_145757_a(ByteBuf p_145757_1_) +- { +- p_145757_1_.writeInt(EntityMinecartCommandBlock.this.getEntityId()); +- } +- public ChunkCoordinates getPlayerCoordinates() +- { +- return new ChunkCoordinates(MathHelper.floor_double(EntityMinecartCommandBlock.this.posX), MathHelper.floor_double(EntityMinecartCommandBlock.this.posY + 0.5D), MathHelper.floor_double(EntityMinecartCommandBlock.this.posZ)); +- } +- public World getEntityWorld() +- { +- return EntityMinecartCommandBlock.this.worldObj; +- } +- }; ++ private final EntityMinecartCommandBlockListener field_145824_a_CB= new EntityMinecartCommandBlockListener(this); // CraftBukkit ++ private final CommandBlockLogic field_145824_a = field_145824_a_CB; // Cauldron ++ + private int field_145823_b = 0; + private static final String __OBFID = "CL_00001672"; + diff --git a/patches/net/minecraft/entity/EntityTracker.java.patch b/patches/net/minecraft/entity/EntityTracker.java.patch new file mode 100644 index 0000000..7381990 --- /dev/null +++ b/patches/net/minecraft/entity/EntityTracker.java.patch @@ -0,0 +1,27 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityTracker.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityTracker.java +@@ -38,6 +38,7 @@ + import net.minecraft.world.chunk.Chunk; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; ++import net.minecraft.server.MinecraftServer; // Spigot + + import cpw.mods.fml.common.registry.EntityRegistry; + +@@ -46,7 +47,7 @@ + private static final Logger logger = LogManager.getLogger(); + private final WorldServer theWorld; + private Set trackedEntities = new HashSet(); +- private IntHashMap trackedEntityIDs = new IntHashMap(); ++ public IntHashMap trackedEntityIDs = new IntHashMap(); // CraftBukkit - private -> public + private int entityViewDistance; + private static final String __OBFID = "CL_00001431"; + +@@ -184,6 +185,7 @@ + + public void addEntityToTracker(Entity p_72785_1_, int p_72785_2_, final int p_72785_3_, boolean p_72785_4_) + { ++ p_72785_2_ = org.spigotmc.TrackingRange.getEntityTrackingRange(p_72785_1_, p_72785_2_); // Spigot + if (p_72785_2_ > this.entityViewDistance) + { + p_72785_2_ = this.entityViewDistance; diff --git a/patches/net/minecraft/entity/EntityTrackerEntry.java.patch b/patches/net/minecraft/entity/EntityTrackerEntry.java.patch new file mode 100644 index 0000000..1963f94 --- /dev/null +++ b/patches/net/minecraft/entity/EntityTrackerEntry.java.patch @@ -0,0 +1,216 @@ +--- ../src-base/minecraft/net/minecraft/entity/EntityTrackerEntry.java ++++ ../src-work/minecraft/net/minecraft/entity/EntityTrackerEntry.java +@@ -56,9 +56,15 @@ + import net.minecraft.world.storage.MapData; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; ++import net.minecraft.server.MinecraftServer; // Spigot + + import cpw.mods.fml.common.network.internal.FMLNetworkHandler; + ++// CraftBukkit start ++import org.bukkit.entity.Player; ++import org.bukkit.event.player.PlayerVelocityEvent; ++// CraftBukkit end ++ + public class EntityTrackerEntry + { + private static final Logger logger = LogManager.getLogger(); +@@ -131,15 +137,15 @@ + this.func_151259_a(new S1BPacketEntityAttach(0, this.myEntity, this.myEntity.ridingEntity)); + } + +- if (this.myEntity instanceof EntityItemFrame && this.ticks % 10 == 0) ++ if (this.myEntity instanceof EntityItemFrame /*&& this.ticks % 10 == 0*/) // CraftBukkit - Moved below, should always enter this block + { + EntityItemFrame entityitemframe = (EntityItemFrame)this.myEntity; + ItemStack itemstack = entityitemframe.getDisplayedItem(); + +- if (itemstack != null && itemstack.getItem() instanceof ItemMap) ++ if (this.ticks % 10 == 0 && itemstack != null && itemstack.getItem() instanceof ItemMap) // CraftBukkit - Moved this.m % 10 logic here so item frames do not enter the other blocks + { + MapData mapdata = Items.filled_map.getMapData(itemstack, this.myEntity.worldObj); +- Iterator iterator = p_73122_1_.iterator(); ++ Iterator iterator = this.trackingPlayers.iterator(); // CraftBukkit + + while (iterator.hasNext()) + { +@@ -177,6 +183,22 @@ + boolean flag = Math.abs(j1) >= 4 || Math.abs(k1) >= 4 || Math.abs(l1) >= 4 || this.ticks % 60 == 0; + boolean flag1 = Math.abs(l - this.lastYaw) >= 4 || Math.abs(i1 - this.lastPitch) >= 4; + ++ // CraftBukkit start - Code moved from below ++ if (flag) ++ { ++ this.lastScaledXPosition = i; ++ this.lastScaledYPosition = j; ++ this.lastScaledZPosition = k; ++ } ++ ++ if (flag1) ++ { ++ this.lastYaw = l; ++ this.lastPitch = i1; ++ } ++ ++ // CraftBukkit end ++ + if (this.ticks > 0 || this.myEntity instanceof EntityArrow) + { + if (j1 >= -128 && j1 < 128 && k1 >= -128 && k1 < 128 && l1 >= -128 && l1 < 128 && this.ticksSinceLastForcedTeleport <= 400 && !this.ridingEntity) +@@ -197,7 +219,15 @@ + else + { + this.ticksSinceLastForcedTeleport = 0; +- object = new S18PacketEntityTeleport(this.myEntity.getEntityId(), i, j, k, (byte)l, (byte)i1); ++ ++ // CraftBukkit start - Refresh list of who can see a player before sending teleport packet ++ if (this.myEntity instanceof EntityPlayerMP) ++ { ++ this.sendEventsToPlayers(new java.util.ArrayList(this.trackingPlayers)); ++ } ++ ++ // CraftBukkit end ++ object = new S18PacketEntityTeleport(this.myEntity.getEntityId(), i, j, k, (byte) l, (byte) i1); + } + } + +@@ -224,7 +254,7 @@ + } + + this.sendMetadataToAllAssociatedPlayers(); +- ++ /* CraftBukkit start - Code moved up + if (flag) + { + this.lastScaledXPosition = i; +@@ -237,7 +267,7 @@ + this.lastYaw = l; + this.lastPitch = i1; + } +- ++ // CraftBukkit end */ + this.ridingEntity = false; + } + else +@@ -275,7 +305,32 @@ + + if (this.myEntity.velocityChanged) + { +- this.func_151261_b(new S12PacketEntityVelocity(this.myEntity)); ++ // CraftBukkit start - Create PlayerVelocity event ++ boolean cancelled = false; ++ ++ if (this.myEntity instanceof EntityPlayerMP) ++ { ++ Player player = (Player) this.myEntity.getBukkitEntity(); ++ org.bukkit.util.Vector velocity = player.getVelocity(); ++ PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity); ++ this.myEntity.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ cancelled = true; ++ } ++ else if (!velocity.equals(event.getVelocity())) ++ { ++ player.setVelocity(velocity); ++ } ++ } ++ ++ if (!cancelled) ++ { ++ this.func_151261_b((Packet)(new S12PacketEntityVelocity(this.myEntity))); ++ } ++ ++ // CraftBukkit end + this.myEntity.velocityChanged = false; + } + } +@@ -296,6 +351,13 @@ + + if (!set.isEmpty()) + { ++ // CraftBukkit start - Send scaled max health ++ if (this.myEntity instanceof EntityPlayerMP) ++ { ++ ((EntityPlayerMP) this.myEntity).getBukkitEntity().injectScaledMaxHealth(set, false); ++ } ++ ++ // CraftBukkit end + this.func_151261_b(new S20PacketEntityProperties(this.myEntity.getEntityId(), set)); + } + +@@ -353,6 +415,19 @@ + + if (d0 >= (double)(-this.blocksDistanceThreshold) && d0 <= (double)this.blocksDistanceThreshold && d1 >= (double)(-this.blocksDistanceThreshold) && d1 <= (double)this.blocksDistanceThreshold) + { ++ // CraftBukkit start ++ if (this.myEntity instanceof EntityPlayerMP) ++ { ++ Player player = ((EntityPlayerMP) this.myEntity).getBukkitEntity(); ++ ++ if (!p_73117_1_.getBukkitEntity().canSee(player)) ++ { ++ return; ++ } ++ } ++ // CraftBukkit end ++ ++ p_73117_1_.destroyedItemsNetCache.remove(Integer.valueOf(this.myEntity.getEntityId())); + if (!this.trackingPlayers.contains(p_73117_1_) && (this.isPlayerWatchingThisChunk(p_73117_1_) || this.myEntity.forceSpawn)) + { + this.trackingPlayers.add(p_73117_1_); +@@ -369,6 +444,13 @@ + ServersideAttributeMap serversideattributemap = (ServersideAttributeMap)((EntityLivingBase)this.myEntity).getAttributeMap(); + Collection collection = serversideattributemap.getWatchedAttributes(); + ++ // CraftBukkit start - If sending own attributes send scaled health instead of current maximum health ++ if (this.myEntity.getEntityId() == p_73117_1_.getEntityId()) ++ { ++ ((EntityPlayerMP) this.myEntity).getBukkitEntity().injectScaledMaxHealth(collection, false); ++ } ++ ++ // CraftBukkit end + if (!collection.isEmpty()) + { + p_73117_1_.playerNetServerHandler.sendPacket(new S20PacketEntityProperties(this.myEntity.getEntityId(), collection)); +@@ -397,6 +479,14 @@ + p_73117_1_.playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(0, this.myEntity, this.myEntity.ridingEntity)); + } + ++ // CraftBukkit start ++ if (this.myEntity.riddenByEntity != null) ++ { ++ p_73117_1_.playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(0, this.myEntity.riddenByEntity, this.myEntity)); ++ } ++ ++ // CraftBukkit end ++ + if (this.myEntity instanceof EntityLiving && ((EntityLiving)this.myEntity).getLeashedToEntity() != null) + { + p_73117_1_.playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(1, this.myEntity, ((EntityLiving)this.myEntity).getLeashedToEntity())); +@@ -425,6 +515,11 @@ + } + } + ++ // CraftBukkit start - Fix for nonsensical head yaw ++ this.lastHeadMotion = MathHelper.floor_float(this.myEntity.getRotationYawHead() * 256.0F / 360.0F); // tracker.ao() should be getHeadRotation ++ this.func_151259_a(new S19PacketEntityHeadLook(this.myEntity, (byte) lastHeadMotion)); ++ // CraftBukkit end ++ + if (this.myEntity instanceof EntityLivingBase) + { + EntityLivingBase entitylivingbase = (EntityLivingBase)this.myEntity; +@@ -465,7 +560,10 @@ + { + if (this.myEntity.isDead) + { +- logger.warn("Fetching addPacket for removed entity"); ++ // CraftBukkit start - Remove useless error spam, just return ++ // logger.warn("Fetching addPacket for removed entity"); ++ return null; ++ // CraftBukkit end + } + + Packet pkt = FMLNetworkHandler.getEntitySpawningPacket(this.myEntity); diff --git a/patches/net/minecraft/entity/ai/EntityAIArrowAttack.java.patch b/patches/net/minecraft/entity/ai/EntityAIArrowAttack.java.patch new file mode 100644 index 0000000..868a77c --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAIArrowAttack.java.patch @@ -0,0 +1,25 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAIArrowAttack.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAIArrowAttack.java +@@ -5,6 +5,11 @@ + import net.minecraft.entity.IRangedAttackMob; + import net.minecraft.util.MathHelper; + ++// CraftBukkit start ++import net.minecraft.entity.Entity; ++import org.bukkit.event.entity.EntityTargetEvent; ++// CraftBukkit end ++ + public class EntityAIArrowAttack extends EntityAIBase + { + private final EntityLiving entityHost; +@@ -67,6 +72,10 @@ + + public void resetTask() + { ++ // CraftBukkit start ++ EntityTargetEvent.TargetReason reason = this.attackTarget.isEntityAlive() ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED; ++ org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent((Entity) rangedAttackEntityHost, null, reason); ++ // CraftBukkit end + this.attackTarget = null; + this.field_75318_f = 0; + this.rangedAttackTime = -1; diff --git a/patches/net/minecraft/entity/ai/EntityAIAttackOnCollide.java.patch b/patches/net/minecraft/entity/ai/EntityAIAttackOnCollide.java.patch new file mode 100644 index 0000000..5487dde --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAIAttackOnCollide.java.patch @@ -0,0 +1,30 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAIAttackOnCollide.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAIAttackOnCollide.java +@@ -7,6 +7,11 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++// CraftBukkit start ++import net.minecraft.entity.Entity; ++import org.bukkit.event.entity.EntityTargetEvent; ++// CraftBukkit end ++ + public class EntityAIAttackOnCollide extends EntityAIBase + { + World worldObj; +@@ -73,6 +78,15 @@ + public boolean continueExecuting() + { + EntityLivingBase entitylivingbase = this.attacker.getAttackTarget(); ++ // CraftBukkit start ++ EntityTargetEvent.TargetReason reason = this.attacker.getAttackTarget() == null ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED; ++ ++ if (this.attacker.getAttackTarget() == null || (this.attacker.getAttackTarget() != null && !this.attacker.getAttackTarget().isEntityAlive())) ++ { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent(attacker, null, reason); ++ } ++ ++ // CraftBukkit end + return entitylivingbase == null ? false : (!entitylivingbase.isEntityAlive() ? false : (!this.longMemory ? !this.attacker.getNavigator().noPath() : this.attacker.isWithinHomeDistance(MathHelper.floor_double(entitylivingbase.posX), MathHelper.floor_double(entitylivingbase.posY), MathHelper.floor_double(entitylivingbase.posZ)))); + } + diff --git a/patches/net/minecraft/entity/ai/EntityAIBreakDoor.java.patch b/patches/net/minecraft/entity/ai/EntityAIBreakDoor.java.patch new file mode 100644 index 0000000..e921440 --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAIBreakDoor.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAIBreakDoor.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAIBreakDoor.java +@@ -58,6 +58,14 @@ + + if (this.breakingTime == 240 && this.theEntity.worldObj.difficultySetting == EnumDifficulty.HARD) + { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreakDoorEvent(this.theEntity, this.entityPosX, this.entityPosY, this.entityPosZ).isCancelled()) ++ { ++ this.updateTask(); ++ return; ++ } ++ ++ // CraftBukkit end + this.theEntity.worldObj.setBlockToAir(this.entityPosX, this.entityPosY, this.entityPosZ); + this.theEntity.worldObj.playAuxSFX(1012, this.entityPosX, this.entityPosY, this.entityPosZ, 0); + this.theEntity.worldObj.playAuxSFX(2001, this.entityPosX, this.entityPosY, this.entityPosZ, Block.getIdFromBlock(this.field_151504_e)); diff --git a/patches/net/minecraft/entity/ai/EntityAIEatGrass.java.patch b/patches/net/minecraft/entity/ai/EntityAIEatGrass.java.patch new file mode 100644 index 0000000..c242e3d --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAIEatGrass.java.patch @@ -0,0 +1,34 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAIEatGrass.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAIEatGrass.java +@@ -6,6 +6,11 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.Material; ++// CraftBukkit end ++ + public class EntityAIEatGrass extends EntityAIBase + { + private EntityLiving field_151500_b; +@@ -69,7 +74,8 @@ + + if (this.field_151501_c.getBlock(i, j, k) == Blocks.tallgrass) + { +- if (this.field_151501_c.getGameRules().getGameRuleBooleanValue("mobGriefing")) ++ // CraftBukkit ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this.field_151500_b, this.field_151500_b.worldObj.getWorld().getBlockAt(i, j, k), Material.AIR, !this.field_151501_c.getGameRules().getGameRuleBooleanValue("mobGriefing")).isCancelled()) + { + this.field_151501_c.func_147480_a(i, j, k, false); + } +@@ -78,7 +84,8 @@ + } + else if (this.field_151501_c.getBlock(i, j - 1, k) == Blocks.grass) + { +- if (this.field_151501_c.getGameRules().getGameRuleBooleanValue("mobGriefing")) ++ // CraftBukkit ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this.field_151500_b, this.field_151500_b.worldObj.getWorld().getBlockAt(i, j - 1, k), Material.DIRT, !this.field_151501_c.getGameRules().getGameRuleBooleanValue("mobGriefing")).isCancelled()) + { + this.field_151501_c.playAuxSFX(2001, i, j - 1, k, Block.getIdFromBlock(Blocks.grass)); + this.field_151501_c.setBlock(i, j - 1, k, Blocks.dirt, 0, 2); diff --git a/patches/net/minecraft/entity/ai/EntityAIMate.java.patch b/patches/net/minecraft/entity/ai/EntityAIMate.java.patch new file mode 100644 index 0000000..8890571 --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAIMate.java.patch @@ -0,0 +1,42 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAIMate.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAIMate.java +@@ -4,6 +4,7 @@ + import java.util.List; + import java.util.Random; + import net.minecraft.entity.EntityAgeable; ++import net.minecraft.entity.EntityLiving; + import net.minecraft.entity.item.EntityXPOrb; + import net.minecraft.entity.passive.EntityAnimal; + import net.minecraft.entity.passive.EntityCow; +@@ -12,6 +13,8 @@ + import net.minecraft.stats.StatList; + import net.minecraft.world.World; + ++import net.minecraft.entity.passive.EntityTameable; // CraftBukkit ++ + public class EntityAIMate extends EntityAIBase + { + private EntityAnimal theAnimal; +@@ -93,6 +96,13 @@ + + if (entityageable != null) + { ++ // CraftBukkit start - set persistence for tame animals ++ if (entityageable instanceof EntityTameable && ((EntityTameable) entityageable).isTamed()) ++ { ++ ((EntityLiving)entityageable).persistenceRequired = true; // Cauldron - fix illegal access error. SS bug? ++ } ++ ++ // CraftBukkit end + EntityPlayer entityplayer = this.theAnimal.func_146083_cb(); + + if (entityplayer == null && this.targetMate.func_146083_cb() != null) +@@ -116,7 +126,7 @@ + this.targetMate.resetInLove(); + entityageable.setGrowingAge(-24000); + entityageable.setLocationAndAngles(this.theAnimal.posX, this.theAnimal.posY, this.theAnimal.posZ, 0.0F, 0.0F); +- this.theWorld.spawnEntityInWorld(entityageable); ++ this.theWorld.addEntity(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason + Random random = this.theAnimal.getRNG(); + + for (int i = 0; i < 7; ++i) diff --git a/patches/net/minecraft/entity/ai/EntityAIPanic.java.patch b/patches/net/minecraft/entity/ai/EntityAIPanic.java.patch new file mode 100644 index 0000000..aef9450 --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAIPanic.java.patch @@ -0,0 +1,26 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAIPanic.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAIPanic.java +@@ -3,6 +3,8 @@ + import net.minecraft.entity.EntityCreature; + import net.minecraft.util.Vec3; + ++import net.minecraft.entity.EntityLivingBase; // CraftBukkit ++ + public class EntityAIPanic extends EntityAIBase + { + private EntityCreature theEntityCreature; +@@ -50,6 +52,14 @@ + + public boolean continueExecuting() + { ++ // CraftBukkit start - introduce a temporary timeout hack until this is fixed properly ++ if ((this.theEntityCreature.ticksExisted - this.theEntityCreature.func_142015_aE()) > 100) ++ { ++ this.theEntityCreature.setRevengeTarget((EntityLivingBase) null); ++ return false; ++ } ++ ++ // CraftBukkit end + return !this.theEntityCreature.getNavigator().noPath(); + } + } diff --git a/patches/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java.patch b/patches/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java.patch new file mode 100644 index 0000000..36c0c25 --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java.patch @@ -0,0 +1,35 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAIRunAroundLikeCrazy.java +@@ -64,7 +64,8 @@ + int i = this.horseHost.getTemper(); + int j = this.horseHost.getMaxTemper(); + +- if (j > 0 && this.horseHost.getRNG().nextInt(j) < i) ++ // CraftBukkit ++ if (j > 0 && this.horseHost.getRNG().nextInt(j) < i && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.horseHost, (EntityPlayer) this.horseHost.riddenByEntity).isCancelled() && this.horseHost.riddenByEntity instanceof EntityPlayer) + { + this.horseHost.setTamedBy((EntityPlayer)this.horseHost.riddenByEntity); + this.horseHost.worldObj.setEntityState(this.horseHost, (byte)7); +@@ -74,8 +75,20 @@ + this.horseHost.increaseTemper(5); + } + +- this.horseHost.riddenByEntity.mountEntity((Entity)null); +- this.horseHost.riddenByEntity = null; ++ // CraftBukkit start - Handle dismounting to account for VehicleExitEvent being fired. ++ if (this.horseHost.riddenByEntity != null) ++ { ++ this.horseHost.riddenByEntity.mountEntity((Entity) null); ++ ++ // If the entity still has a passenger, then a plugin cancelled the event. ++ if (this.horseHost.riddenByEntity != null) ++ { ++ return; ++ } ++ } ++ ++ // this.entity.passenger = null; ++ // CraftBukkit end + this.horseHost.makeHorseRearWithSound(); + this.horseHost.worldObj.setEntityState(this.horseHost, (byte)6); + } diff --git a/patches/net/minecraft/entity/ai/EntityAISit.java.patch b/patches/net/minecraft/entity/ai/EntityAISit.java.patch new file mode 100644 index 0000000..cfb4697 --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAISit.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAISit.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAISit.java +@@ -19,7 +19,7 @@ + { + if (!this.theEntity.isTamed()) + { +- return false; ++ return this.isSitting && this.theEntity.getAttackTarget() == null; // CraftBukkit - Allow sitting for wild animals + } + else if (this.theEntity.isInWater()) + { diff --git a/patches/net/minecraft/entity/ai/EntityAITarget.java.patch b/patches/net/minecraft/entity/ai/EntityAITarget.java.patch new file mode 100644 index 0000000..da6c042 --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAITarget.java.patch @@ -0,0 +1,66 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAITarget.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAITarget.java +@@ -12,6 +12,11 @@ + import net.minecraft.util.MathHelper; + import org.apache.commons.lang3.StringUtils; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.event.entity.EntityTargetEvent; ++// CraftBukkit end ++ + public abstract class EntityAITarget extends EntityAIBase + { + protected EntityCreature taskOwner; +@@ -156,6 +161,51 @@ + } + } + ++ // CraftBukkit start - Check all the different target goals for the reason, default to RANDOM_TARGET ++ EntityTargetEvent.TargetReason reason = EntityTargetEvent.TargetReason.RANDOM_TARGET; ++ ++ if (this instanceof EntityAIDefendVillage) ++ { ++ reason = EntityTargetEvent.TargetReason.DEFEND_VILLAGE; ++ } ++ else if (this instanceof EntityAIHurtByTarget) ++ { ++ reason = EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY; ++ } ++ else if (this instanceof EntityAINearestAttackableTarget) ++ { ++ if (p_75296_1_ instanceof EntityPlayer) ++ { ++ reason = EntityTargetEvent.TargetReason.CLOSEST_PLAYER; ++ } ++ } ++ else if (this instanceof EntityAIOwnerHurtByTarget) ++ { ++ reason = EntityTargetEvent.TargetReason.TARGET_ATTACKED_OWNER; ++ } ++ else if (this instanceof EntityAIOwnerHurtTarget) ++ { ++ reason = EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET; ++ } ++ ++ org.bukkit.event.entity.EntityTargetLivingEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetLivingEvent(this.taskOwner, p_75296_1_, reason); ++ ++ if (event.isCancelled() || event.getTarget() == null) ++ { ++ this.taskOwner.setAttackTarget(null); ++ return false; ++ } ++ else if (p_75296_1_.getBukkitEntity() != event.getTarget()) ++ { ++ this.taskOwner.setAttackTarget((EntityLivingBase)((CraftEntity) event.getTarget()).getHandle()); ++ } ++ ++ if (this.taskOwner instanceof EntityCreature) ++ { ++ ((EntityCreature) this.taskOwner).entityToAttack = ((CraftEntity) event.getTarget()).getHandle(); ++ } ++ ++ // CraftBukkit end + return true; + } + } diff --git a/patches/net/minecraft/entity/ai/EntityAIVillagerMate.java.patch b/patches/net/minecraft/entity/ai/EntityAIVillagerMate.java.patch new file mode 100644 index 0000000..7da3ed5 --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityAIVillagerMate.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityAIVillagerMate.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityAIVillagerMate.java +@@ -119,7 +119,7 @@ + this.villagerObj.setGrowingAge(6000); + entityvillager.setGrowingAge(-24000); + entityvillager.setLocationAndAngles(this.villagerObj.posX, this.villagerObj.posY, this.villagerObj.posZ, 0.0F, 0.0F); +- this.worldObj.spawnEntityInWorld(entityvillager); ++ this.worldObj.addEntity(entityvillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason + this.worldObj.setEntityState(entityvillager, (byte)12); + } + } diff --git a/patches/net/minecraft/entity/ai/EntityLookHelper.java.patch b/patches/net/minecraft/entity/ai/EntityLookHelper.java.patch new file mode 100644 index 0000000..856003a --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityLookHelper.java.patch @@ -0,0 +1,24 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityLookHelper.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityLookHelper.java +@@ -5,6 +5,8 @@ + import net.minecraft.entity.EntityLivingBase; + import net.minecraft.util.MathHelper; + ++import org.bukkit.craftbukkit.TrigMath; // CraftBukkit ++ + public class EntityLookHelper + { + private EntityLiving entity; +@@ -61,8 +63,10 @@ + double d1 = this.posY - (this.entity.posY + (double)this.entity.getEyeHeight()); + double d2 = this.posZ - this.entity.posZ; + double d3 = (double)MathHelper.sqrt_double(d0 * d0 + d2 * d2); +- float f = (float)(Math.atan2(d2, d0) * 180.0D / Math.PI) - 90.0F; +- float f1 = (float)(-(Math.atan2(d1, d3) * 180.0D / Math.PI)); ++ // CraftBukkit start - Math -> TrigMath ++ float f = (float)(TrigMath.atan2(d2, d0) * 180.0D / Math.PI) - 90.0F; ++ float f1 = (float)(-(TrigMath.atan2(d1, d3) * 180.0D / Math.PI)); ++ // CraftBukkit end + this.entity.rotationPitch = this.updateRotation(this.entity.rotationPitch, f1, this.deltaLookPitch); + this.entity.rotationYawHead = this.updateRotation(this.entity.rotationYawHead, f, this.deltaLookYaw); + } diff --git a/patches/net/minecraft/entity/ai/EntityMoveHelper.java.patch b/patches/net/minecraft/entity/ai/EntityMoveHelper.java.patch new file mode 100644 index 0000000..467bcd6 --- /dev/null +++ b/patches/net/minecraft/entity/ai/EntityMoveHelper.java.patch @@ -0,0 +1,12 @@ +--- ../src-base/minecraft/net/minecraft/entity/ai/EntityMoveHelper.java ++++ ../src-work/minecraft/net/minecraft/entity/ai/EntityMoveHelper.java +@@ -56,7 +56,8 @@ + + if (d3 >= 2.500000277905201E-7D) + { +- float f = (float)(Math.atan2(d1, d0) * 180.0D / Math.PI) - 90.0F; ++ // CraftBukkit - Math -> TrigMath ++ float f = (float)(org.bukkit.craftbukkit.TrigMath.atan2(d1, d0) * 180.0D / Math.PI) - 90.0F; + this.entity.rotationYaw = this.limitAngle(this.entity.rotationYaw, f, 30.0F); + this.entity.setAIMoveSpeed((float)(this.speed * this.entity.getEntityAttribute(SharedMonsterAttributes.movementSpeed).getAttributeValue())); + diff --git a/patches/net/minecraft/entity/boss/EntityDragon.java.patch b/patches/net/minecraft/entity/boss/EntityDragon.java.patch new file mode 100644 index 0000000..781ef89 --- /dev/null +++ b/patches/net/minecraft/entity/boss/EntityDragon.java.patch @@ -0,0 +1,258 @@ +--- ../src-base/minecraft/net/minecraft/entity/boss/EntityDragon.java ++++ ../src-work/minecraft/net/minecraft/entity/boss/EntityDragon.java +@@ -22,6 +22,19 @@ + import net.minecraft.world.Explosion; + import net.minecraft.world.World; + ++// CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.network.play.server.S23PacketBlockChange; ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.util.BlockStateListPopulator; ++import org.bukkit.event.entity.EntityCreatePortalEvent; ++import org.bukkit.event.entity.EntityExplodeEvent; ++import org.bukkit.event.entity.EntityRegainHealthEvent; ++import org.bukkit.event.entity.EntityTargetEvent; ++import org.bukkit.Bukkit; ++// CraftBukkit end ++ + public class EntityDragon extends EntityLiving implements IBossDisplayData, IEntityMultiPart, IMob + { + public double targetX; +@@ -44,6 +57,7 @@ + private Entity target; + public int deathTicks; + public EntityEnderCrystal healingEnderCrystal; ++ private Explosion explosionSource = new Explosion(null, this, Double.NaN, Double.NaN, Double.NaN, Float.NaN); // CraftBukkit - reusable source for CraftTNTPrimed.getSource() + private static final String __OBFID = "CL_00001659"; + + public EntityDragon(World p_i1700_1_) +@@ -355,14 +369,25 @@ + { + if (!this.worldObj.isRemote) + { +- this.attackEntityFromPart(this.dragonPartHead, DamageSource.setExplosionSource((Explosion)null), 10.0F); ++ CraftEventFactory.entityDamage = this.healingEnderCrystal; // CraftBukkit ++ this.attackEntityFromPart(this.dragonPartHead, DamageSource.setExplosionSource((Explosion) null), 10.0F); ++ CraftEventFactory.entityDamage = null; // CraftBukkit + } + + this.healingEnderCrystal = null; + } + else if (this.ticksExisted % 10 == 0 && this.getHealth() < this.getMaxHealth()) + { +- this.setHealth(this.getHealth() + 1.0F); ++ // CraftBukkit start ++ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), 1.0D, EntityRegainHealthEvent.RegainReason.ENDER_CRYSTAL); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.setHealth((float)(this.getHealth() + event.getAmount())); ++ } ++ ++ // CraftBukkit end + } + } + +@@ -429,7 +454,24 @@ + + if (this.rand.nextInt(2) == 0 && !this.worldObj.playerEntities.isEmpty()) + { +- this.target = (Entity)this.worldObj.playerEntities.get(this.rand.nextInt(this.worldObj.playerEntities.size())); ++ // CraftBukkit start ++ Entity target = (Entity) this.worldObj.playerEntities.get(this.rand.nextInt(this.worldObj.playerEntities.size())); ++ EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), target.getBukkitEntity(), EntityTargetEvent.TargetReason.RANDOM_TARGET); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ if (event.getTarget() == null) ++ { ++ this.target = null; ++ } ++ else ++ { ++ this.target = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); ++ } ++ } ++ ++ // CraftBukkit end + } + else + { +@@ -468,6 +510,10 @@ + int j1 = MathHelper.floor_double(p_70972_1_.maxZ); + boolean flag = false; + boolean flag1 = false; ++ // CraftBukkit start - Create a list to hold all the destroyed blocks ++ List destroyedBlocks = new java.util.ArrayList(); ++ org.bukkit.craftbukkit.CraftWorld craftWorld = this.worldObj.getWorld(); ++ // CraftBukkit end + + for (int k1 = i; k1 <= l; ++k1) + { +@@ -481,7 +527,11 @@ + { + if (block.canEntityDestroy(worldObj, k1, l1, i2, this) && this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")) + { +- flag1 = this.worldObj.setBlockToAir(k1, l1, i2) || flag1; ++ // CraftBukkit start - Add blocks to list rather than destroying them ++ // flag1 = this.world.setAir(k1, l1, i2) || flag1; ++ flag1 = true; ++ destroyedBlocks.add(craftWorld.getBlockAt(k1, l1, i2)); ++ // CraftBukkit end + } + else + { +@@ -494,6 +544,52 @@ + + if (flag1) + { ++ // CraftBukkit start - Set off an EntityExplodeEvent for the dragon exploding all these blocks ++ org.bukkit.entity.Entity bukkitEntity = this.getBukkitEntity(); ++ EntityExplodeEvent event = new EntityExplodeEvent(bukkitEntity, bukkitEntity.getLocation(), destroyedBlocks, 0F); ++ Bukkit.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ // This flag literally means 'Dragon hit something hard' (Obsidian, White Stone or Bedrock) and will cause the dragon to slow down. ++ // We should consider adding an event extension for it, or perhaps returning true if the event is cancelled. ++ return flag; ++ } ++ else if (event.getYield() == 0F) ++ { ++ // Yield zero ==> no drops ++ for (org.bukkit.block.Block block : event.blockList()) ++ { ++ this.worldObj.setBlockToAir(block.getX(), block.getY(), block.getZ()); ++ } ++ } ++ else ++ { ++ for (org.bukkit.block.Block block : event.blockList()) ++ { ++ org.bukkit.Material blockId = block.getType(); ++ ++ if (blockId == org.bukkit.Material.AIR) ++ { ++ continue; ++ } ++ ++ int blockX = block.getX(); ++ int blockY = block.getY(); ++ int blockZ = block.getZ(); ++ Block nmsBlock = org.bukkit.craftbukkit.util.CraftMagicNumbers.getBlock(blockId); ++ ++ if (nmsBlock.canDropFromExplosion(explosionSource)) ++ { ++ nmsBlock.dropBlockAsItemWithChance(this.worldObj, blockX, blockY, blockZ, block.getData(), event.getYield(), 0); ++ } ++ ++ nmsBlock.onBlockDestroyedByExplosion(worldObj, blockX, blockY, blockZ, explosionSource); ++ this.worldObj.setBlockToAir(blockX, blockY, blockZ); ++ } ++ } ++ ++ // CraftBukkit end + double d1 = p_70972_1_.minX + (p_70972_1_.maxX - p_70972_1_.minX) * (double)this.rand.nextFloat(); + double d2 = p_70972_1_.minY + (p_70972_1_.maxY - p_70972_1_.minY) * (double)this.rand.nextFloat(); + double d0 = p_70972_1_.minZ + (p_70972_1_.maxZ - p_70972_1_.minZ) * (double)this.rand.nextFloat(); +@@ -531,13 +627,18 @@ + return false; + } + +- protected boolean func_82195_e(DamageSource p_82195_1_, float p_82195_2_) ++ public boolean func_82195_e(DamageSource p_82195_1_, float p_82195_2_) // CraftBukkit - protected -> public + { + return super.attackEntityFrom(p_82195_1_, p_82195_2_); + } + + protected void onDeathUpdate() + { ++ if (this.isDead) ++ { ++ return; // CraftBukkit - can't kill what's already dead ++ } ++ + ++this.deathTicks; + + if (this.deathTicks >= 180 && this.deathTicks <= 200) +@@ -555,7 +656,7 @@ + { + if (this.deathTicks > 150 && this.deathTicks % 5 == 0) + { +- i = 1000; ++ i = this.expToDrop / 12; // CraftBukkit - drop experience as dragon falls from sky. use experience drop from death event. This is now set in getExpReward() + + while (i > 0) + { +@@ -576,7 +677,7 @@ + + if (this.deathTicks == 200 && !this.worldObj.isRemote) + { +- i = 2000; ++ i = this.expToDrop - (10 * this.expToDrop / 12); // CraftBukkit - drop the remaining experience + + while (i > 0) + { +@@ -595,6 +696,8 @@ + byte b0 = 64; + BlockEndPortal.field_149948_a = true; + byte b1 = 4; ++ // CraftBukkit start - Replace any "this.world" in the following with just "world"! ++ BlockStateListPopulator world = new BlockStateListPopulator(this.worldObj.getWorld()); + + for (int k = b0 - 1; k <= b0 + 32; ++k) + { +@@ -641,6 +744,35 @@ + this.worldObj.setBlock(p_70975_1_, b0 + 2, p_70975_2_ + 1, Blocks.torch); + this.worldObj.setBlock(p_70975_1_, b0 + 3, p_70975_2_, Blocks.bedrock); + this.worldObj.setBlock(p_70975_1_, b0 + 4, p_70975_2_, Blocks.dragon_egg); ++ EntityCreatePortalEvent event = new EntityCreatePortalEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), java.util.Collections.unmodifiableList(world.getList()), org.bukkit.PortalType.ENDER); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ for (BlockState state : event.getBlocks()) ++ { ++ state.update(true); ++ } ++ } ++ else ++ { ++ for (BlockState state : event.getBlocks()) ++ { ++ S23PacketBlockChange packet = new S23PacketBlockChange(state.getX(), state.getY(), state.getZ(), this.worldObj); ++ ++ for (Iterator it = this.worldObj.playerEntities.iterator(); it.hasNext();) ++ { ++ EntityPlayer entity = (EntityPlayer) it.next(); ++ ++ if (entity instanceof EntityPlayerMP) ++ { ++ ((EntityPlayerMP) entity).playerNetServerHandler.sendPacket(packet); ++ } ++ } ++ } ++ } ++ ++ // CraftBukkit end + BlockEndPortal.field_149948_a = false; + } + +@@ -675,4 +807,13 @@ + { + return 5.0F; + } ++ ++ // CraftBukkit start ++ public int getExpReward() ++ { ++ // This value is equal to the amount of experience dropped while falling from the sky (10 * 1000) ++ // plus what is dropped when the dragon hits the ground (2000) ++ return 12000; ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/entity/boss/EntityWither.java.patch b/patches/net/minecraft/entity/boss/EntityWither.java.patch new file mode 100644 index 0000000..62083a2 --- /dev/null +++ b/patches/net/minecraft/entity/boss/EntityWither.java.patch @@ -0,0 +1,57 @@ +--- ../src-base/minecraft/net/minecraft/entity/boss/EntityWither.java ++++ ../src-work/minecraft/net/minecraft/entity/boss/EntityWither.java +@@ -34,6 +34,11 @@ + import net.minecraft.world.EnumDifficulty; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.ExplosionPrimeEvent; ++// CraftBukkit end ++ + public class EntityWither extends EntityMob implements IBossDisplayData, IRangedAttackMob + { + private float[] field_82220_d = new float[2]; +@@ -228,15 +233,25 @@ + + if (i <= 0) + { +- this.worldObj.newExplosion(this, this.posX, this.posY + (double)this.getEyeHeight(), this.posZ, 7.0F, false, this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")); +- this.worldObj.playBroadcastSound(1013, (int)this.posX, (int)this.posY, (int)this.posZ, 0); ++ // CraftBukkit start ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 7.0F, false); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.worldObj.newExplosion(this, this.posX, this.posY + (double) this.getEyeHeight(), this.posZ, event.getRadius(), event.getFire(), this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")); ++ } ++ ++ // CraftBukkit end ++ this.worldObj.newExplosion(this, this.posX, this.posY + (double) this.getEyeHeight(), this.posZ, 7.0F, false, this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")); ++ this.worldObj.playBroadcastSound(1013, (int) this.posX, (int) this.posY, (int) this.posZ, 0); + } + + this.func_82215_s(i); + + if (this.ticksExisted % 10 == 0) + { +- this.heal(10.0F); ++ this.heal(10.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit + } + } + else +@@ -349,6 +364,13 @@ + + if (!block.isAir(worldObj, j2, k, l) && block.canEntityDestroy(worldObj, j2, k, l, this)) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, j2, k, l, Blocks.air, 0).isCancelled()) ++ { ++ continue; ++ } ++ ++ // CraftBukkit end + flag = this.worldObj.func_147480_a(j2, k, l, true) || flag; + } + } diff --git a/patches/net/minecraft/entity/effect/EntityLightningBolt.java.patch b/patches/net/minecraft/entity/effect/EntityLightningBolt.java.patch new file mode 100644 index 0000000..7d54c84 --- /dev/null +++ b/patches/net/minecraft/entity/effect/EntityLightningBolt.java.patch @@ -0,0 +1,100 @@ +--- ../src-base/minecraft/net/minecraft/entity/effect/EntityLightningBolt.java ++++ ../src-work/minecraft/net/minecraft/entity/effect/EntityLightningBolt.java +@@ -10,6 +10,8 @@ + import net.minecraft.world.EnumDifficulty; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntityLightningBolt extends EntityWeatherEffect + { + private int lightningState; +@@ -17,15 +19,25 @@ + private int boltLivingTime; + private static final String __OBFID = "CL_00001666"; + ++ // CraftBukkit start ++ public boolean isEffect = false; ++ + public EntityLightningBolt(World p_i1703_1_, double p_i1703_2_, double p_i1703_4_, double p_i1703_6_) + { ++ this(p_i1703_1_, p_i1703_2_, p_i1703_4_, p_i1703_6_, false); ++ } ++ ++ public EntityLightningBolt(World p_i1703_1_, double p_i1703_2_, double p_i1703_4_, double p_i1703_6_, boolean isEffect) ++ { + super(p_i1703_1_); ++ this.isEffect = isEffect; ++ // CraftBukkit end + this.setLocationAndAngles(p_i1703_2_, p_i1703_4_, p_i1703_6_, 0.0F, 0.0F); + this.lightningState = 2; + this.boltVertex = this.rand.nextLong(); + this.boltLivingTime = this.rand.nextInt(3) + 1; + +- if (!p_i1703_1_.isRemote && p_i1703_1_.getGameRules().getGameRuleBooleanValue("doFireTick") && (p_i1703_1_.difficultySetting == EnumDifficulty.NORMAL || p_i1703_1_.difficultySetting == EnumDifficulty.HARD) && p_i1703_1_.doChunksNearChunkExist(MathHelper.floor_double(p_i1703_2_), MathHelper.floor_double(p_i1703_4_), MathHelper.floor_double(p_i1703_6_), 10)) ++ if (!isEffect && !p_i1703_1_.isRemote && p_i1703_1_.getGameRules().getGameRuleBooleanValue("doFireTick") && (p_i1703_1_.difficultySetting == EnumDifficulty.NORMAL || p_i1703_1_.difficultySetting == EnumDifficulty.HARD) && p_i1703_1_.doChunksNearChunkExist(MathHelper.floor_double(p_i1703_2_), MathHelper.floor_double(p_i1703_4_), MathHelper.floor_double(p_i1703_6_), 10)) // CraftBukkit + { + int i = MathHelper.floor_double(p_i1703_2_); + int j = MathHelper.floor_double(p_i1703_4_); +@@ -33,7 +45,13 @@ + + if (p_i1703_1_.getBlock(i, j, k).getMaterial() == Material.air && Blocks.fire.canPlaceBlockAt(p_i1703_1_, i, j, k)) + { +- p_i1703_1_.setBlock(i, j, k, Blocks.fire); ++ // CraftBukkit start ++ if (!CraftEventFactory.callBlockIgniteEvent(p_i1703_1_, i, j, k, this).isCancelled()) ++ { ++ p_i1703_1_.setBlock(i, j, k, Blocks.fire); ++ } ++ ++ // CraftBukkit end + } + + for (i = 0; i < 4; ++i) +@@ -44,7 +62,13 @@ + + if (p_i1703_1_.getBlock(j, k, l).getMaterial() == Material.air && Blocks.fire.canPlaceBlockAt(p_i1703_1_, j, k, l)) + { +- p_i1703_1_.setBlock(j, k, l, Blocks.fire); ++ // CraftBukkit start ++ if (!CraftEventFactory.callBlockIgniteEvent(p_i1703_1_, j, k, l, this).isCancelled()) ++ { ++ p_i1703_1_.setBlock(j, k, l, Blocks.fire); ++ } ++ ++ // CraftBukkit end + } + } + } +@@ -74,7 +98,8 @@ + this.lightningState = 1; + this.boltVertex = this.rand.nextLong(); + +- if (!this.worldObj.isRemote && this.worldObj.getGameRules().getGameRuleBooleanValue("doFireTick") && this.worldObj.doChunksNearChunkExist(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ), 10)) ++ // CraftBukkit ++ if (!isEffect && !this.worldObj.isRemote && this.worldObj.getGameRules().getGameRuleBooleanValue("doFireTick") && this.worldObj.doChunksNearChunkExist(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ), 10)) + { + int i = MathHelper.floor_double(this.posX); + int j = MathHelper.floor_double(this.posY); +@@ -82,13 +107,19 @@ + + if (this.worldObj.getBlock(i, j, k).getMaterial() == Material.air && Blocks.fire.canPlaceBlockAt(this.worldObj, i, j, k)) + { +- this.worldObj.setBlock(i, j, k, Blocks.fire); ++ // CraftBukkit start ++ if (!CraftEventFactory.callBlockIgniteEvent(worldObj, i, j, k, this).isCancelled()) ++ { ++ this.worldObj.setBlock(i, j, k, Blocks.fire); ++ } ++ ++ // CraftBukkit end + } + } + } + } + +- if (this.lightningState >= 0) ++ if (this.lightningState >= 0 && !this.isEffect) // CraftBukkit - add !this.isEffect + { + if (this.worldObj.isRemote) + { diff --git a/patches/net/minecraft/entity/item/EntityBoat.java.patch b/patches/net/minecraft/entity/item/EntityBoat.java.patch new file mode 100644 index 0000000..b2b026a --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityBoat.java.patch @@ -0,0 +1,239 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityBoat.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityBoat.java +@@ -17,6 +17,16 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.entity.Vehicle; ++import org.bukkit.event.vehicle.VehicleDamageEvent; ++import org.bukkit.event.vehicle.VehicleDestroyEvent; ++import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; ++import org.bukkit.event.vehicle.VehicleMoveEvent; ++// CraftBukkit end ++ + public class EntityBoat extends Entity + { + private boolean isBoatEmpty; +@@ -35,6 +45,32 @@ + private double velocityZ; + private static final String __OBFID = "CL_00001667"; + ++ // CraftBukkit start ++ public double maxSpeed = 0.4D; ++ public double occupiedDeceleration = 0.2D; ++ public double unoccupiedDeceleration = -1; ++ public boolean landBoats = false; ++ ++ @Override ++ ++ /** ++ * Applies a velocity to each of the entities pushing them away from each other. Args: entity ++ */ ++ public void applyEntityCollision(Entity entity) ++ { ++ org.bukkit.entity.Entity hitEntity = (entity == null) ? null : entity.getBukkitEntity(); ++ VehicleEntityCollisionEvent event = new VehicleEntityCollisionEvent((Vehicle) this.getBukkitEntity(), hitEntity); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ super.applyEntityCollision(entity); ++ } ++ // CraftBukkit end ++ + public EntityBoat(World p_i1704_1_) + { + super(p_i1704_1_); +@@ -82,6 +118,7 @@ + this.prevPosX = p_i1705_2_; + this.prevPosY = p_i1705_4_; + this.prevPosZ = p_i1705_6_; ++ this.worldObj.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleCreateEvent((Vehicle) this.getBukkitEntity())); // CraftBukkit + } + + public double getMountedYOffset() +@@ -97,6 +134,19 @@ + } + else if (!this.worldObj.isRemote && !this.isDead) + { ++ // CraftBukkit start ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ org.bukkit.entity.Entity attacker = (p_70097_1_.getEntity() == null) ? null : p_70097_1_.getEntity().getBukkitEntity(); ++ VehicleDamageEvent event = new VehicleDamageEvent(vehicle, attacker, (double) p_70097_2_); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return true; ++ } ++ ++ // f = event.getDamage(); // TODO Why don't we do this? ++ // CraftBukkit end + this.setForwardDirection(-this.getForwardDirection()); + this.setTimeSinceHit(10); + this.setDamageTaken(this.getDamageTaken() + p_70097_2_ * 10.0F); +@@ -105,6 +155,18 @@ + + if (flag || this.getDamageTaken() > 40.0F) + { ++ // CraftBukkit start ++ VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, attacker); ++ this.worldObj.getServer().getPluginManager().callEvent(destroyEvent); ++ ++ if (destroyEvent.isCancelled()) ++ { ++ this.setDamageTaken(40F); // Maximize damage so this doesn't get triggered again right away ++ return true; ++ } ++ ++ // CraftBukkit end ++ + if (this.riddenByEntity != null) + { + this.riddenByEntity.mountEntity(this); +@@ -181,6 +243,13 @@ + + public void onUpdate() + { ++ // CraftBukkit start ++ double prevX = this.posX; ++ double prevY = this.posY; ++ double prevZ = this.posZ; ++ float prevYaw = this.rotationYaw; ++ float prevPitch = this.rotationPitch; ++ // CraftBukkit end + super.onUpdate(); + + if (this.getTimeSinceHit() > 0) +@@ -303,7 +372,25 @@ + this.motionX += -Math.sin((double)(f * (float)Math.PI / 180.0F)) * this.speedMultiplier * (double)entitylivingbase.moveForward * 0.05000000074505806D; + this.motionZ += Math.cos((double)(f * (float)Math.PI / 180.0F)) * this.speedMultiplier * (double)entitylivingbase.moveForward * 0.05000000074505806D; + } ++ // CraftBukkit start - Support unoccupied deceleration ++ else if (unoccupiedDeceleration >= 0) ++ { ++ this.motionX *= unoccupiedDeceleration; ++ this.motionZ *= unoccupiedDeceleration; + ++ // Kill lingering speed ++ if (motionX <= 0.00001) ++ { ++ motionX = 0; ++ } ++ ++ if (motionZ <= 0.00001) ++ { ++ motionZ = 0; ++ } ++ } ++ ++ // CraftBukkit end + d2 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ); + + if (d2 > 0.35D) +@@ -347,18 +434,32 @@ + + if (block == Blocks.snow_layer) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, i1, k, j, Blocks.air, 0).isCancelled()) ++ { ++ continue; ++ } ++ ++ // CraftBukkit end + this.worldObj.setBlockToAir(i1, k, j); + this.isCollidedHorizontally = false; + } + else if (block == Blocks.waterlily) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, i1, k, j, Blocks.air, 0).isCancelled()) ++ { ++ continue; ++ } ++ ++ // CraftBukkit end + this.worldObj.func_147480_a(i1, k, j, true); + this.isCollidedHorizontally = false; + } + } + } + +- if (this.onGround) ++ if (this.onGround && !this.landBoats) // CraftBukkit + { + this.motionX *= 0.5D; + this.motionY *= 0.5D; +@@ -371,17 +472,27 @@ + { + if (!this.worldObj.isRemote && !this.isDead) + { +- this.setDead(); ++ // CraftBukkit start ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, null); ++ this.worldObj.getServer().getPluginManager().callEvent(destroyEvent); + +- for (l = 0; l < 3; ++l) ++ if (!destroyEvent.isCancelled()) + { +- this.func_145778_a(Item.getItemFromBlock(Blocks.planks), 1, 0.0F); +- } ++ this.setDead(); + +- for (l = 0; l < 2; ++l) +- { +- this.func_145778_a(Items.stick, 1, 0.0F); ++ for (l = 0; l < 3; ++l) ++ { ++ this.func_145778_a(Item.getItemFromBlock(Blocks.planks), 1, 0.0F); ++ } ++ ++ for (l = 0; l < 2; ++l) ++ { ++ this.func_145778_a(Items.stick, 1, 0.0F); ++ } + } ++ ++ // CraftBukkit end + } + } + else +@@ -415,7 +526,22 @@ + + this.rotationYaw = (float)((double)this.rotationYaw + d7); + this.setRotation(this.rotationYaw, this.rotationPitch); ++ // CraftBukkit start ++ org.bukkit.Server server = this.worldObj.getServer(); ++ org.bukkit.World bworld = this.worldObj.getWorld(); ++ Location from = new Location(bworld, prevX, prevY, prevZ, prevYaw, prevPitch); ++ Location to = new Location(bworld, this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ server.getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle)); + ++ if (!from.equals(to)) ++ { ++ VehicleMoveEvent event = new VehicleMoveEvent(vehicle, from, to); ++ server.getPluginManager().callEvent(event); ++ } ++ ++ // CraftBukkit end ++ + if (!this.worldObj.isRemote) + { + List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.expand(0.20000000298023224D, 0.0D, 0.20000000298023224D)); +@@ -435,6 +561,7 @@ + + if (this.riddenByEntity != null && this.riddenByEntity.isDead) + { ++ this.riddenByEntity.ridingEntity = null; // CraftBukkit + this.riddenByEntity = null; + } + } diff --git a/patches/net/minecraft/entity/item/EntityEnderCrystal.java.patch b/patches/net/minecraft/entity/item/EntityEnderCrystal.java.patch new file mode 100644 index 0000000..33daf81 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityEnderCrystal.java.patch @@ -0,0 +1,49 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityEnderCrystal.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityEnderCrystal.java +@@ -10,6 +10,8 @@ + import net.minecraft.world.World; + import net.minecraft.world.WorldProviderEnd; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntityEnderCrystal extends Entity + { + public int innerRotation; +@@ -56,7 +58,13 @@ + + if (this.worldObj.provider instanceof WorldProviderEnd && this.worldObj.getBlock(i, j, k) != Blocks.fire) + { +- this.worldObj.setBlock(i, j, k, Blocks.fire); ++ // CraftBukkit start ++ if (!CraftEventFactory.callBlockIgniteEvent(this.worldObj, i, j, k, this).isCancelled()) ++ { ++ this.worldObj.setBlock(i, j, k, Blocks.fire); ++ } ++ ++ // CraftBukkit end + } + } + +@@ -85,6 +93,13 @@ + { + if (!this.isDead && !this.worldObj.isRemote) + { ++ // CraftBukkit start - All non-living entities need this ++ if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, p_70097_1_, p_70097_2_)) ++ { ++ return false; ++ } ++ ++ // CraftBukkit end + this.health = 0; + + if (this.health <= 0) +@@ -93,7 +108,7 @@ + + if (!this.worldObj.isRemote) + { +- this.worldObj.createExplosion((Entity)null, this.posX, this.posY, this.posZ, 6.0F, true); ++ this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, 6.0F, true); // CraftBukkit - (Entity) null -> this + } + } + } diff --git a/patches/net/minecraft/entity/item/EntityEnderPearl.java.patch b/patches/net/minecraft/entity/item/EntityEnderPearl.java.patch new file mode 100644 index 0000000..113f501 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityEnderPearl.java.patch @@ -0,0 +1,56 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityEnderPearl.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityEnderPearl.java +@@ -12,6 +12,12 @@ + import net.minecraftforge.common.MinecraftForge; + import net.minecraftforge.event.entity.living.EnderTeleportEvent; + ++// CraftBukkit start ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.player.PlayerTeleportEvent; ++// CraftBukkit end ++ + public class EntityEnderPearl extends EntityThrowable + { + private static final String __OBFID = "CL_00001725"; +@@ -52,18 +58,31 @@ + + if (entityplayermp.playerNetServerHandler.func_147362_b().isChannelOpen() && entityplayermp.worldObj == this.worldObj) + { +- EnderTeleportEvent event = new EnderTeleportEvent(entityplayermp, this.posX, this.posY, this.posZ, 5.0F); +- if (!MinecraftForge.EVENT_BUS.post(event)) +- { // Don't indent to lower patch size +- if (this.getThrower().isRiding()) +- { +- this.getThrower().mountEntity((Entity)null); ++ EnderTeleportEvent event = new EnderTeleportEvent(entityplayermp, this.posX, this.posY, this.posZ, 5); ++ // Cauldron start - invert condition; return if cancelled otherwise fall through to CB event ++ if (MinecraftForge.EVENT_BUS.post(event)){ ++ this.setDead(); ++ return; + } ++ // Cauldron end ++ // CraftBukkit start ++ org.bukkit.craftbukkit.entity.CraftPlayer player = entityplayermp.getBukkitEntity(); ++ org.bukkit.Location location = getBukkitEntity().getLocation(); ++ location.setPitch(player.getLocation().getPitch()); ++ location.setYaw(player.getLocation().getYaw()); ++ PlayerTeleportEvent teleEvent = new PlayerTeleportEvent(player, player.getLocation(), location, PlayerTeleportEvent.TeleportCause.ENDER_PEARL); ++ Bukkit.getPluginManager().callEvent(teleEvent); + +- this.getThrower().setPositionAndUpdate(event.targetX, event.targetY, event.targetZ); +- this.getThrower().fallDistance = 0.0F; +- this.getThrower().attackEntityFrom(DamageSource.fall, event.attackDamage); ++ if (!teleEvent.isCancelled() && !entityplayermp.playerNetServerHandler.isDisconnected()) ++ { ++ entityplayermp.playerNetServerHandler.teleport(teleEvent.getTo()); ++ this.getThrower().fallDistance = 0.0F; ++ CraftEventFactory.entityDamage = this; ++ this.getThrower().attackEntityFrom(DamageSource.fall, 5.0F); ++ CraftEventFactory.entityDamage = null; + } ++ ++ // CraftBukkit end + } + } + diff --git a/patches/net/minecraft/entity/item/EntityExpBottle.java.patch b/patches/net/minecraft/entity/item/EntityExpBottle.java.patch new file mode 100644 index 0000000..bcf22d0 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityExpBottle.java.patch @@ -0,0 +1,24 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityExpBottle.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityExpBottle.java +@@ -43,9 +43,20 @@ + { + if (!this.worldObj.isRemote) + { +- this.worldObj.playAuxSFX(2002, (int)Math.round(this.posX), (int)Math.round(this.posY), (int)Math.round(this.posZ), 0); ++ // CraftBukkit moved after event ++ //this.worldObj.playAuxSFX(2002, (int)Math.round(this.posX), (int)Math.round(this.posY), (int)Math.round(this.posZ), 0); + int i = 3 + this.worldObj.rand.nextInt(5) + this.worldObj.rand.nextInt(5); ++ // CraftBukkit start ++ org.bukkit.event.entity.ExpBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExpBottleEvent(this, i); ++ i = event.getExperience(); + ++ if (event.getShowEffect()) ++ { ++ this.worldObj.playAuxSFX(2002, (int) Math.round(this.posX), (int) Math.round(this.posY), (int) Math.round(this.posZ), 0); ++ } ++ ++ // CraftBukkit end ++ + while (i > 0) + { + int j = EntityXPOrb.getXPSplit(i); diff --git a/patches/net/minecraft/entity/item/EntityFallingBlock.java.patch b/patches/net/minecraft/entity/item/EntityFallingBlock.java.patch new file mode 100644 index 0000000..b2d2850 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityFallingBlock.java.patch @@ -0,0 +1,57 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityFallingBlock.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityFallingBlock.java +@@ -19,9 +19,11 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntityFallingBlock extends Entity + { +- private Block field_145811_e; ++ public Block field_145811_e; // CraftBukkit - private -> public + public int field_145814_a; + public int field_145812_b; + public boolean field_145813_c; +@@ -103,7 +105,8 @@ + + if (this.field_145812_b == 1) + { +- if (this.worldObj.getBlock(i, j, k) != this.field_145811_e) ++ // CraftBukkit - compare data and call event ++ if (this.field_145812_b != 1 || this.worldObj.getBlock(i, j, k) != this.field_145811_e || this.worldObj.getBlockMetadata(i, j, k) != this.field_145814_a || CraftEventFactory.callEntityChangeBlockEvent(this, i, j, k, Blocks.air, 0).isCancelled()) + { + this.setDead(); + return; +@@ -122,8 +125,17 @@ + { + this.setDead(); + +- if (!this.field_145808_f && this.worldObj.canPlaceEntityOnSide(this.field_145811_e, i, j, k, true, 1, (Entity)null, (ItemStack)null) && !BlockFalling.func_149831_e(this.worldObj, i, j - 1, k) && this.worldObj.setBlock(i, j, k, this.field_145811_e, this.field_145814_a, 3)) ++ // CraftBukkit start ++ if (!this.field_145808_f && this.worldObj.canPlaceEntityOnSide(this.field_145811_e, i, j, k, true, 1, (Entity) null, (ItemStack) null) && !BlockFalling.func_149831_e(this.worldObj, i, j - 1, k) /* mimic the false conditions of setTypeIdAndData */ && i >= -30000000 && k >= -30000000 && i < 30000000 && k < 30000000 && j > 0 && j < 256 && !(this.worldObj.getBlock(i, j, k) == this.field_145811_e && this.worldObj.getBlockMetadata(i, j, k) == this.field_145814_a)) + { ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, i, j, k, this.field_145811_e, this.field_145814_a).isCancelled()) ++ { ++ return; ++ } ++ ++ this.worldObj.setBlock(i, j, k, this.field_145811_e, this.field_145814_a, 3); ++ // CraftBukkit end ++ + if (this.field_145811_e instanceof BlockFalling) + { + ((BlockFalling)this.field_145811_e).func_149828_a(this.worldObj, i, j, k, this.field_145814_a); +@@ -189,8 +201,10 @@ + + while (iterator.hasNext()) + { +- Entity entity = (Entity)iterator.next(); +- entity.attackEntityFrom(damagesource, (float)Math.min(MathHelper.floor_float((float)i * this.field_145816_i), this.field_145815_h)); ++ Entity entity = (Entity) iterator.next(); ++ CraftEventFactory.entityDamage = this; // CraftBukkit ++ entity.attackEntityFrom(damagesource,(float) Math.min(MathHelper.floor_float((float) i * this.field_145816_i), this.field_145815_h)); ++ CraftEventFactory.entityDamage = null; // CraftBukkit + } + + if (flag && (double)this.rand.nextFloat() < 0.05000000074505806D + (double)i * 0.05D) diff --git a/patches/net/minecraft/entity/item/EntityFireworkRocket.java.patch b/patches/net/minecraft/entity/item/EntityFireworkRocket.java.patch new file mode 100644 index 0000000..f0ce86b --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityFireworkRocket.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityFireworkRocket.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityFireworkRocket.java +@@ -11,7 +11,7 @@ + public class EntityFireworkRocket extends Entity + { + private int fireworkAge; +- private int lifetime; ++ public int lifetime; // CraftBukkit - private -> public + private static final String __OBFID = "CL_00001718"; + + public EntityFireworkRocket(World p_i1762_1_) diff --git a/patches/net/minecraft/entity/item/EntityItem.java.patch b/patches/net/minecraft/entity/item/EntityItem.java.patch new file mode 100644 index 0000000..02441af --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityItem.java.patch @@ -0,0 +1,280 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityItem.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityItem.java +@@ -22,6 +22,11 @@ + import cpw.mods.fml.common.FMLCommonHandler; + import cpw.mods.fml.common.eventhandler.Event.Result; + ++// CraftBukkit start ++import org.bukkit.event.player.PlayerPickupItemEvent; ++import net.minecraft.server.MinecraftServer; ++// CraftBukkit end ++ + public class EntityItem extends Entity + { + private static final Logger logger = LogManager.getLogger(); +@@ -31,6 +36,7 @@ + private String field_145801_f; + private String field_145802_g; + public float hoverStart; ++ private int lastTick = MinecraftServer.currentTick; // CraftBukkit + private static final String __OBFID = "CL_00001669"; + + /** +@@ -55,6 +61,15 @@ + public EntityItem(World p_i1710_1_, double p_i1710_2_, double p_i1710_4_, double p_i1710_6_, ItemStack p_i1710_8_) + { + this(p_i1710_1_, p_i1710_2_, p_i1710_4_, p_i1710_6_); ++ ++ // CraftBukkit start - Can't set null items in the datawatcher ++ if (p_i1710_8_ == null || p_i1710_8_.getItem() == null) ++ { ++ return; ++ } ++ ++ // CraftBukkit end ++ + this.setEntityItemStack(p_i1710_8_); + this.lifespan = (p_i1710_8_.getItem() == null ? 6000 : p_i1710_8_.getItem().getEntityLifespan(p_i1710_8_, p_i1710_1_)); + } +@@ -89,93 +104,102 @@ + } + } + +- if (this.getEntityItem() == null) +- { +- this.setDead(); ++ super.onUpdate(); ++ // CraftBukkit start - Use wall time for pickup and despawn timers ++ int elapsedTicks = MinecraftServer.currentTick - this.lastTick; ++ this.delayBeforeCanPickup -= elapsedTicks; ++ this.age += elapsedTicks; ++ this.lastTick = MinecraftServer.currentTick; ++ // CraftBukkit end ++ ++ boolean forceUpdate = this.ticksExisted > 0 && this.ticksExisted % 25 == 0; // Cauldron - optimize item tick updates ++ this.prevPosX = this.posX; ++ this.prevPosY = this.posY; ++ this.prevPosZ = this.posZ; ++ this.motionY -= 0.03999999910593033D; ++ // Cauldron start - if forced ++ if (forceUpdate || noClip) { ++ this.noClip = this.func_145771_j(this.posX, (this.boundingBox.minY + this.boundingBox.maxY) / 2.0D, this.posZ); + } +- else +- { +- super.onUpdate(); ++ // Cauldron end ++ this.moveEntity(this.motionX, this.motionY, this.motionZ); ++ boolean flag = (int)this.prevPosX != (int)this.posX || (int)this.prevPosY != (int)this.posY || (int)this.prevPosZ != (int)this.posZ; + +- if (this.delayBeforeCanPickup > 0) ++ if ((flag && this.ticksExisted % 5 == 0) || forceUpdate) // Cauldron - if forced ++ { ++ if (this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)).getMaterial() == Material.lava) + { +- --this.delayBeforeCanPickup; ++ this.motionY = 0.20000000298023224D; ++ this.motionX = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F); ++ this.motionZ = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F); ++ this.playSound("random.fizz", 0.4F, 2.0F + this.rand.nextFloat() * 0.4F); + } + +- this.prevPosX = this.posX; +- this.prevPosY = this.posY; +- this.prevPosZ = this.posZ; +- this.motionY -= 0.03999999910593033D; +- this.noClip = this.func_145771_j(this.posX, (this.boundingBox.minY + this.boundingBox.maxY) / 2.0D, this.posZ); +- this.moveEntity(this.motionX, this.motionY, this.motionZ); +- boolean flag = (int)this.prevPosX != (int)this.posX || (int)this.prevPosY != (int)this.posY || (int)this.prevPosZ != (int)this.posZ; +- +- if (flag || this.ticksExisted % 25 == 0) ++ if (forceUpdate && !this.worldObj.isRemote) // Cauldron - if forced + { +- if (this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ)).getMaterial() == Material.lava) +- { +- this.motionY = 0.20000000298023224D; +- this.motionX = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F); +- this.motionZ = (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F); +- this.playSound("random.fizz", 0.4F, 2.0F + this.rand.nextFloat() * 0.4F); +- } +- +- if (!this.worldObj.isRemote) +- { +- this.searchForOtherItemsNearby(); +- } ++ this.searchForOtherItemsNearby(); + } ++ } + +- float f = 0.98F; ++ float f = 0.98F; + +- if (this.onGround) +- { +- f = this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)).slipperiness * 0.98F; +- } ++ if (this.onGround) ++ { ++ f = this.worldObj.getBlock(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.boundingBox.minY) - 1, MathHelper.floor_double(this.posZ)).slipperiness * 0.98F; ++ } + +- this.motionX *= (double)f; +- this.motionY *= 0.9800000190734863D; +- this.motionZ *= (double)f; ++ this.motionX *= (double)f; ++ this.motionY *= 0.9800000190734863D; ++ this.motionZ *= (double)f; + +- if (this.onGround) +- { +- this.motionY *= -0.5D; +- } ++ if (this.onGround) ++ { ++ this.motionY *= -0.5D; ++ } + +- ++this.age; ++ // ++this.age; // CraftBukkit - Moved up (base age on wall time) + +- ItemStack item = getDataWatcher().getWatchableObjectItemStack(10); +- +- if (!this.worldObj.isRemote && this.age >= lifespan) ++ ItemStack item = getDataWatcher().getWatchableObjectItemStack(10); ++ ++ if (!this.worldObj.isRemote && this.age >= lifespan - 1) // Cauldron adjust for age being off by one when it is first dropped ++ { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) + { +- if (item != null) +- { +- ItemExpireEvent event = new ItemExpireEvent(this, (item.getItem() == null ? 6000 : item.getItem().getEntityLifespan(item, worldObj))); +- if (MinecraftForge.EVENT_BUS.post(event)) +- { +- lifespan += event.extraLife; +- } +- else +- { +- this.setDead(); +- } ++ this.age = 0; ++ return; ++ } ++ // CraftBukkit end ++ if (item != null) ++ { ++ ItemExpireEvent event = new ItemExpireEvent(this, (item.getItem() == null ? this.worldObj.getSpigotConfig().itemDespawnRate : item.getItem().getEntityLifespan(item, worldObj))); // Spigot // Cauldron ++ if (MinecraftForge.EVENT_BUS.post(event)) ++ { ++ lifespan += event.extraLife; + } + else + { + this.setDead(); + } + } +- +- if (item != null && item.stackSize <= 0) ++ else + { + this.setDead(); + } + } ++ ++ if (item != null && item.stackSize <= 0) ++ { ++ this.setDead(); ++ } + } + + private void searchForOtherItemsNearby() + { +- Iterator iterator = this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.boundingBox.expand(0.5D, 0.0D, 0.5D)).iterator(); ++ // Spigot start ++ double radius = worldObj.getSpigotConfig().itemMerge; // Cauldron ++ Iterator iterator = this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.boundingBox.expand(radius, radius, radius)).iterator(); ++ // Spigot end + + while (iterator.hasNext()) + { +@@ -225,11 +249,13 @@ + } + else + { +- itemstack1.stackSize += itemstack.stackSize; +- p_70289_1_.delayBeforeCanPickup = Math.max(p_70289_1_.delayBeforeCanPickup, this.delayBeforeCanPickup); +- p_70289_1_.age = Math.min(p_70289_1_.age, this.age); +- p_70289_1_.setEntityItemStack(itemstack1); +- this.setDead(); ++ // Spigot start ++ itemstack.stackSize += itemstack1.stackSize; ++ this.delayBeforeCanPickup = Math.max(p_70289_1_.delayBeforeCanPickup, this.delayBeforeCanPickup); ++ this.age = Math.min(p_70289_1_.age, this.age); ++ this.setEntityItemStack(itemstack); ++ p_70289_1_.setDead(); ++ // Spigot end + return true; + } + } +@@ -316,8 +342,27 @@ + } + + NBTTagCompound nbttagcompound1 = p_70037_1_.getCompoundTag("Item"); +- this.setEntityItemStack(ItemStack.loadItemStackFromNBT(nbttagcompound1)); + ++ // CraftBukkit start ++ if (nbttagcompound1 != null) ++ { ++ ItemStack itemstack = ItemStack.loadItemStackFromNBT(nbttagcompound1); ++ ++ if (itemstack != null) ++ { ++ this.setEntityItemStack(itemstack); ++ } ++ else ++ { ++ this.setDead(); ++ } ++ } ++ else ++ { ++ this.setDead(); ++ } ++ ++ // CraftBukkit end + ItemStack item = getDataWatcher().getWatchableObjectItemStack(10); + + if (item == null || item.stackSize <= 0) +@@ -350,6 +395,31 @@ + ItemStack itemstack = this.getEntityItem(); + int i = itemstack.stackSize; + ++ // CraftBukkit start ++ int canHold = p_70100_1_.inventory.canHold(itemstack); ++ int remaining = itemstack.stackSize - canHold; ++ ++ if (this.delayBeforeCanPickup <= 0 && canHold > 0) ++ { ++ itemstack.stackSize = canHold; ++ // Cauldron start - rename to cbEvent to fix naming collision ++ PlayerPickupItemEvent cbEvent = new PlayerPickupItemEvent((org.bukkit.entity.Player) p_70100_1_.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); ++ //cbEvent.setCancelled(!par1EntityPlayer.canPickUpLoot); TODO ++ this.worldObj.getServer().getPluginManager().callEvent(cbEvent); ++ itemstack.stackSize = canHold + remaining; ++ ++ if (cbEvent.isCancelled()) ++ { ++ return; ++ } ++ // Cauldron end ++ ++ // Possibly < 0; fix here so we do not have to modify code below ++ this.delayBeforeCanPickup = 0; ++ } ++ ++ // CraftBukkit end ++ + if (this.delayBeforeCanPickup <= 0 && (this.field_145802_g == null || lifespan - this.age <= 200 || this.field_145802_g.equals(p_70100_1_.getCommandSenderName())) && (event.getResult() == Result.ALLOW || i <= 0 || p_70100_1_.inventory.addItemStackToInventory(itemstack))) + { + if (itemstack.getItem() == Item.getItemFromBlock(Blocks.log)) diff --git a/patches/net/minecraft/entity/item/EntityItemFrame.java.patch b/patches/net/minecraft/entity/item/EntityItemFrame.java.patch new file mode 100644 index 0000000..a862230 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityItemFrame.java.patch @@ -0,0 +1,16 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityItemFrame.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityItemFrame.java +@@ -45,6 +45,13 @@ + { + if (!this.worldObj.isRemote) + { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, p_70097_1_, p_70097_2_) || this.isDead) ++ { ++ return true; ++ } ++ // CraftBukkit end ++ + this.func_146065_b(p_70097_1_.getEntity(), false); + this.setDisplayedItem((ItemStack)null); + } diff --git a/patches/net/minecraft/entity/item/EntityMinecart.java.patch b/patches/net/minecraft/entity/item/EntityMinecart.java.patch new file mode 100644 index 0000000..d082841 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityMinecart.java.patch @@ -0,0 +1,234 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityMinecart.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityMinecart.java +@@ -28,6 +28,15 @@ + import net.minecraftforge.event.entity.minecart.MinecartCollisionEvent; + import net.minecraftforge.event.entity.minecart.MinecartUpdateEvent; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.entity.Vehicle; ++import org.bukkit.event.vehicle.VehicleDamageEvent; ++import org.bukkit.event.vehicle.VehicleDestroyEvent; ++import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; ++import org.bukkit.util.Vector; ++// CraftBukkit end ++ + public abstract class EntityMinecart extends Entity + { + private boolean isInReverse; +@@ -47,6 +56,17 @@ + private double velocityZ; + private static final String __OBFID = "CL_00001670"; + ++ // CraftBukkit start ++ public boolean slowWhenEmpty = true; ++ private double derailedX = 0.5D; ++ private double derailedY = 0.5D; ++ private double derailedZ = 0.5D; ++ private double flyingX = 0.95D; ++ private double flyingY = 0.95D; ++ private double flyingZ = 0.95D; ++ public double maxSpeed = 0.4D; ++ // CraftBukkit end ++ + /* Forge: Minecart Compatibility Layer Integration. */ + public static float defaultMaxSpeedAirLateral = 0.4f; + public static float defaultMaxSpeedAirVertical = -1f; +@@ -138,6 +158,7 @@ + this.prevPosX = p_i1713_2_; + this.prevPosY = p_i1713_4_; + this.prevPosZ = p_i1713_6_; ++ this.worldObj.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleCreateEvent((Vehicle) this.getBukkitEntity())); // CraftBukkit + } + + public double getMountedYOffset() +@@ -155,6 +176,19 @@ + } + else + { ++ // CraftBukkit start ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ org.bukkit.entity.Entity passenger = (p_70097_1_.getEntity() == null) ? null : p_70097_1_.getEntity().getBukkitEntity(); ++ VehicleDamageEvent event = new VehicleDamageEvent(vehicle, passenger, p_70097_2_); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return true; ++ } ++ ++ p_70097_2_ = (float) event.getDamage(); ++ // CraftBukkit end + this.setRollingDirection(-this.getRollingDirection()); + this.setRollingAmplitude(10); + this.setBeenAttacked(); +@@ -168,6 +202,18 @@ + this.riddenByEntity.mountEntity(this); + } + ++ // CraftBukkit start ++ VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, passenger); ++ this.worldObj.getServer().getPluginManager().callEvent(destroyEvent); ++ ++ if (destroyEvent.isCancelled()) ++ { ++ this.setDamage(40); // Maximize damage so this doesn't ++ // get triggered again right away ++ return true; ++ } ++ // CraftBukkit end ++ + if (flag && !this.hasCustomInventoryName()) + { + this.setDead(); +@@ -220,6 +266,14 @@ + + public void onUpdate() + { ++ // CraftBukkit start ++ double prevX = this.posX; ++ double prevY = this.posY; ++ double prevZ = this.posZ; ++ float prevYaw = this.rotationYaw; ++ float prevPitch = this.rotationPitch; ++ // CraftBukkit end ++ + if (this.getRollingAmplitude() > 0) + { + this.setRollingAmplitude(this.getRollingAmplitude() - 1); +@@ -245,7 +299,7 @@ + + if (this.inPortal) + { +- if (minecraftserver.getAllowNether()) ++ if (true || minecraftserver.getAllowNether()) // CraftBukkit - multi-world should still allow teleport even if default vanilla nether disabled + { + if (this.ridingEntity == null && this.portalCounter++ >= i) + { +@@ -324,7 +378,7 @@ + --i; + } + +- double d0 = 0.4D; ++ double d0 = this.maxSpeed; // CraftBukkit + double d2 = 0.0078125D; + Block block = this.worldObj.getBlock(l, i, i1); + +@@ -368,7 +422,18 @@ + } + + this.setRotation(this.rotationYaw, this.rotationPitch); ++ // CraftBukkit start ++ org.bukkit.World bworld = this.worldObj.getWorld(); ++ Location from = new Location(bworld, prevX, prevY, prevZ, prevYaw, prevPitch); ++ Location to = new Location(bworld, this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ this.worldObj.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle)); + ++ if (!from.equals(to)) ++ { ++ this.worldObj.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to)); ++ } ++ // CraftBukkit end + AxisAlignedBB box; + if (getCollisionHandler() != null) + { +@@ -445,18 +510,22 @@ + + if (this.onGround) + { +- this.motionX *= 0.5D; +- this.motionY *= 0.5D; +- this.motionZ *= 0.5D; ++ // CraftBukkit start ++ this.motionX *= this.derailedX; ++ this.motionY *= this.derailedY; ++ this.motionZ *= this.derailedZ; ++ // CraftBukkit end + } + + this.moveEntity(this.motionX, moveY, this.motionZ); + + if (!this.onGround) + { ++ // CraftBukkit start // Cauldron - CB changed to flyingX but Forge changed to getDragAir() - prefer Forge in this case + this.motionX *= getDragAir(); + this.motionY *= getDragAir(); + this.motionZ *= getDragAir(); ++ // CraftBukkit end + } + } + +@@ -678,7 +747,7 @@ + + protected void applyDrag() + { +- if (this.riddenByEntity != null) ++ if (this.riddenByEntity != null || !this.slowWhenEmpty) // CraftBukkit + { + this.motionX *= 0.996999979019165D; + this.motionY *= 0.0D; +@@ -866,6 +935,18 @@ + { + if (p_70108_1_ != this.riddenByEntity) + { ++ // CraftBukkit start ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ org.bukkit.entity.Entity hitEntity = (p_70108_1_ == null) ? null : p_70108_1_.getBukkitEntity(); ++ VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, hitEntity); ++ this.worldObj.getServer().getPluginManager().callEvent(collisionEvent); ++ ++ if (collisionEvent.isCancelled()) ++ { ++ return; ++ } ++ // CraftBukkit end ++ + if (p_70108_1_ instanceof EntityLivingBase && !(p_70108_1_ instanceof EntityPlayer) && !(p_70108_1_ instanceof EntityIronGolem) && canBeRidden() && this.motionX * this.motionX + this.motionZ * this.motionZ > 0.01D && this.riddenByEntity == null && p_70108_1_.ridingEntity == null) + { + p_70108_1_.mountEntity(this); +@@ -875,7 +956,8 @@ + double d1 = p_70108_1_.posZ - this.posZ; + double d2 = d0 * d0 + d1 * d1; + +- if (d2 >= 9.999999747378752E-5D) ++ // CraftBukkit - collision ++ if (d2 >= 9.999999747378752E-5D && !collisionEvent.isCollisionCancelled()) + { + d2 = (double)MathHelper.sqrt_double(d2); + d0 /= d2; +@@ -1089,6 +1171,34 @@ + { + return this.entityName; + } ++ ++ // CraftBukkit start - Methods for getting and setting flying and derailed ++ // velocity modifiers ++ public Vector getFlyingVelocityMod() ++ { ++ return new Vector(flyingX, flyingY, flyingZ); ++ } ++ ++ public void setFlyingVelocityMod(Vector flying) ++ { ++ flyingX = flying.getX(); ++ flyingY = flying.getY(); ++ flyingZ = flying.getZ(); ++ } ++ ++ public Vector getDerailedVelocityMod() ++ { ++ return new Vector(derailedX, derailedY, derailedZ); ++ } ++ ++ public void setDerailedVelocityMod(Vector derailed) ++ { ++ derailedX = derailed.getX(); ++ derailedY = derailed.getY(); ++ derailedZ = derailed.getZ(); ++ } ++ // CraftBukkit end ++ + /* =================================== FORGE START ===========================================*/ + /** + * Moved to allow overrides. diff --git a/patches/net/minecraft/entity/item/EntityMinecartContainer.java.patch b/patches/net/minecraft/entity/item/EntityMinecartContainer.java.patch new file mode 100644 index 0000000..0827277 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityMinecartContainer.java.patch @@ -0,0 +1,78 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityMinecartContainer.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityMinecartContainer.java +@@ -9,12 +9,61 @@ + import net.minecraft.util.DamageSource; + import net.minecraft.world.World; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.inventory.InventoryHolder; ++// CraftBukkit end ++ + public abstract class EntityMinecartContainer extends EntityMinecart implements IInventory + { + private ItemStack[] minecartContainerItems = new ItemStack[36]; + private boolean dropContentsWhenDead = true; + private static final String __OBFID = "CL_00001674"; + ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return this.minecartContainerItems; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public InventoryHolder getOwner() ++ { ++ org.bukkit.entity.Entity cart = getBukkitEntity(); ++ ++ if (cart instanceof InventoryHolder) ++ { ++ return (InventoryHolder) cart; ++ } ++ ++ return null; ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end ++ + public EntityMinecartContainer(World p_i1716_1_) + { + super(p_i1716_1_); +@@ -147,6 +196,13 @@ + + public void travelToDimension(int p_71027_1_) + { ++ // Spigot Start ++ for (HumanEntity human : new java.util.ArrayList(transaction)) ++ { ++ human.closeInventory(); ++ } ++ ++ // Spigot End + this.dropContentsWhenDead = false; + super.travelToDimension(p_71027_1_); + } diff --git a/patches/net/minecraft/entity/item/EntityPainting.java.patch b/patches/net/minecraft/entity/item/EntityPainting.java.patch new file mode 100644 index 0000000..b9c5842 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityPainting.java.patch @@ -0,0 +1,10 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityPainting.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityPainting.java +@@ -19,6 +19,7 @@ + public EntityPainting(World p_i1599_1_) + { + super(p_i1599_1_); ++ this.art = EntityPainting.EnumArt.values()[this.rand.nextInt(EntityPainting.EnumArt.values().length)]; // CraftBukkit - generate a non-null painting + } + + public EntityPainting(World p_i1600_1_, int p_i1600_2_, int p_i1600_3_, int p_i1600_4_, int p_i1600_5_) diff --git a/patches/net/minecraft/entity/item/EntityTNTPrimed.java.patch b/patches/net/minecraft/entity/item/EntityTNTPrimed.java.patch new file mode 100644 index 0000000..de44908 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityTNTPrimed.java.patch @@ -0,0 +1,57 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityTNTPrimed.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityTNTPrimed.java +@@ -7,11 +7,15 @@ + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit ++ + public class EntityTNTPrimed extends Entity + { + public int fuse; + private EntityLivingBase tntPlacedBy; + private static final String __OBFID = "CL_00001681"; ++ public float yield = 4; // CraftBukkit ++ public boolean isIncendiary = false; // CraftBukkit + + public EntityTNTPrimed(World p_i1729_1_) + { +@@ -68,12 +72,14 @@ + + if (this.fuse-- <= 0) + { +- this.setDead(); +- ++ // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event + if (!this.worldObj.isRemote) + { + this.explode(); + } ++ ++ this.setDead(); ++ // CraftBukkit end + } + else + { +@@ -83,8 +89,19 @@ + + private void explode() + { +- float f = 4.0F; +- this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, f, true); ++ // CraftBukkit start ++ // float f = 4.0F; ++ org.bukkit.craftbukkit.CraftServer server = this.worldObj.getServer(); ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(server, this)); ++ server.getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ // give 'this' instead of (Entity) null so we know what causes the damage ++ this.worldObj.newExplosion(this, this.posX, this.posY, this.posZ, event.getRadius(), event.getFire(), true); ++ } ++ ++ // CraftBukkit end + } + + protected void writeEntityToNBT(NBTTagCompound p_70014_1_) diff --git a/patches/net/minecraft/entity/item/EntityXPOrb.java.patch b/patches/net/minecraft/entity/item/EntityXPOrb.java.patch new file mode 100644 index 0000000..5aeae47 --- /dev/null +++ b/patches/net/minecraft/entity/item/EntityXPOrb.java.patch @@ -0,0 +1,158 @@ +--- ../src-base/minecraft/net/minecraft/entity/item/EntityXPOrb.java ++++ ../src-work/minecraft/net/minecraft/entity/item/EntityXPOrb.java +@@ -12,13 +12,18 @@ + import net.minecraftforge.common.MinecraftForge; + import net.minecraftforge.event.entity.player.PlayerPickupXpEvent; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityTargetEvent; ++// CraftBukkit end ++ + public class EntityXPOrb extends Entity + { + public int xpColor; + public int xpOrbAge; + public int field_70532_c; + private int xpOrbHealth = 5; +- public int xpValue; ++ public int xpValue; // CraftBukkit - private -> public + private EntityPlayer closestPlayer; + private int xpTargetColor; + private static final String __OBFID = "CL_00001544"; +@@ -115,18 +120,27 @@ + + if (this.closestPlayer != null) + { +- double d1 = (this.closestPlayer.posX - this.posX) / d0; +- double d2 = (this.closestPlayer.posY + (double)this.closestPlayer.getEyeHeight() - this.posY) / d0; +- double d3 = (this.closestPlayer.posZ - this.posZ) / d0; +- double d4 = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3); +- double d5 = 1.0D - d4; ++ // CraftBukkit start ++ EntityTargetEvent event = CraftEventFactory.callEntityTargetEvent(this, closestPlayer, EntityTargetEvent.TargetReason.CLOSEST_PLAYER); ++ Entity target = event.getTarget() == null ? null : ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); + +- if (d5 > 0.0D) ++ if (!event.isCancelled() && target != null) + { +- d5 *= d5; +- this.motionX += d1 / d4 * d5 * 0.1D; +- this.motionY += d2 / d4 * d5 * 0.1D; +- this.motionZ += d3 / d4 * d5 * 0.1D; ++ double d1 = (target.posX - this.posX) / d0; ++ double d2 = (target.posY + (double) target.getEyeHeight() - this.posY) / d0; ++ double d3 = (target.posZ - this.posZ) / d0; ++ double d4 = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3); ++ double d5 = 1.0D - d4; ++ ++ if (d5 > 0.0D) ++ { ++ d5 *= d5; ++ this.motionX += d1 / d4 * d5 * 0.1D; ++ this.motionY += d2 / d4 * d5 * 0.1D; ++ this.motionZ += d3 / d4 * d5 * 0.1D; ++ } ++ ++ // CraftBukkit end + } + } + +@@ -210,7 +224,7 @@ + p_70100_1_.xpCooldown = 2; + this.worldObj.playSoundAtEntity(p_70100_1_, "random.orb", 0.1F, 0.5F * ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.8F)); + p_70100_1_.onItemPickup(this, 1); +- p_70100_1_.addExperience(this.xpValue); ++ p_70100_1_.addExperience(CraftEventFactory.callPlayerExpChangeEvent(p_70100_1_, this.xpValue).getAmount()); + this.setDead(); + } + } +@@ -229,6 +243,88 @@ + + public static int getXPSplit(int p_70527_0_) + { ++ // CraftBukkit start ++ if (p_70527_0_ > 162670129) ++ { ++ return p_70527_0_ - 100000; ++ } ++ ++ if (p_70527_0_ > 81335063) ++ { ++ return 81335063; ++ } ++ ++ if (p_70527_0_ > 40667527) ++ { ++ return 40667527; ++ } ++ ++ if (p_70527_0_ > 20333759) ++ { ++ return 20333759; ++ } ++ ++ if (p_70527_0_ > 10166857) ++ { ++ return 10166857; ++ } ++ ++ if (p_70527_0_ > 5083423) ++ { ++ return 5083423; ++ } ++ ++ if (p_70527_0_ > 2541701) ++ { ++ return 2541701; ++ } ++ ++ if (p_70527_0_ > 1270849) ++ { ++ return 1270849; ++ } ++ ++ if (p_70527_0_ > 635413) ++ { ++ return 635413; ++ } ++ ++ if (p_70527_0_ > 317701) ++ { ++ return 317701; ++ } ++ ++ if (p_70527_0_ > 158849) ++ { ++ return 158849; ++ } ++ ++ if (p_70527_0_ > 79423) ++ { ++ return 79423; ++ } ++ ++ if (p_70527_0_ > 39709) ++ { ++ return 39709; ++ } ++ ++ if (p_70527_0_ > 19853) ++ { ++ return 19853; ++ } ++ ++ if (p_70527_0_ > 9923) ++ { ++ return 9923; ++ } ++ ++ if (p_70527_0_ > 4957) ++ { ++ return 4957; ++ } ++ ++ // CraftBukkit end + return p_70527_0_ >= 2477 ? 2477 : (p_70527_0_ >= 1237 ? 1237 : (p_70527_0_ >= 617 ? 617 : (p_70527_0_ >= 307 ? 307 : (p_70527_0_ >= 149 ? 149 : (p_70527_0_ >= 73 ? 73 : (p_70527_0_ >= 37 ? 37 : (p_70527_0_ >= 17 ? 17 : (p_70527_0_ >= 7 ? 7 : (p_70527_0_ >= 3 ? 3 : 1))))))))); + } + diff --git a/patches/net/minecraft/entity/monster/EntityCreeper.java.patch b/patches/net/minecraft/entity/monster/EntityCreeper.java.patch new file mode 100644 index 0000000..c4c0d3a --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntityCreeper.java.patch @@ -0,0 +1,78 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntityCreeper.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntityCreeper.java +@@ -23,6 +23,11 @@ + import net.minecraft.util.DamageSource; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.ExplosionPrimeEvent; ++// CraftBukkit end ++ + public class EntityCreeper extends EntityMob + { + private int lastActiveTime; +@@ -207,9 +212,35 @@ + public void onStruckByLightning(EntityLightningBolt p_70077_1_) + { + super.onStruckByLightning(p_70077_1_); +- this.dataWatcher.updateObject(17, Byte.valueOf((byte)1)); ++ ++ // Cauldron start ++ if (p_70077_1_ != null) ++ { ++ // CraftBukkit start ++ if (CraftEventFactory.callCreeperPowerEvent(this, p_70077_1_, org.bukkit.event.entity.CreeperPowerEvent.PowerCause.LIGHTNING).isCancelled()) ++ { ++ return; ++ } ++ } ++ // Cauldron end ++ ++ this.setPowered(true); + } + ++ public void setPowered(boolean powered) ++ { ++ if (!powered) ++ { ++ this.dataWatcher.updateObject(17, Byte.valueOf((byte) 0)); ++ } ++ else ++ { ++ this.dataWatcher.updateObject(17, Byte.valueOf((byte) 1)); ++ } ++ ++ // CraftBukkit end ++ } ++ + protected boolean interact(EntityPlayer p_70085_1_) + { + ItemStack itemstack = p_70085_1_.inventory.getCurrentItem(); +@@ -235,17 +266,22 @@ + if (!this.worldObj.isRemote) + { + boolean flag = this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing"); ++ // CraftBukkit start ++ float radius = this.getPowered() ? 6.0F : 3.0F; ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), radius, false); ++ this.worldObj.getServer().getPluginManager().callEvent(event); + +- if (this.getPowered()) ++ if (!event.isCancelled()) + { +- this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, (float)(this.explosionRadius * 2), flag); ++ this.worldObj.newExplosion(this, this.posX, this.posY, this.posZ, event.getRadius(), event.getFire(), flag); ++ this.setDead(); + } + else + { +- this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, (float)this.explosionRadius, flag); ++ this.timeSinceIgnited = 0; + } + +- this.setDead(); ++ // CraftBukkit end + } + } + diff --git a/patches/net/minecraft/entity/monster/EntityEnderman.java.patch b/patches/net/minecraft/entity/monster/EntityEnderman.java.patch new file mode 100644 index 0000000..ebc90de --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntityEnderman.java.patch @@ -0,0 +1,72 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntityEnderman.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntityEnderman.java +@@ -24,6 +24,12 @@ + import net.minecraftforge.common.MinecraftForge; + import net.minecraftforge.event.entity.living.EnderTeleportEvent; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityTeleportEvent; ++// CraftBukkit end ++ + public class EntityEnderman extends EntityMob + { + private static final UUID attackingSpeedBoostModifierUUID = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0"); +@@ -161,9 +167,15 @@ + + if (EntityEnderman.getCarriable(block)) + { +- this.func_146081_a(block); +- this.setCarryingData(this.worldObj.getBlockMetadata(k, i, j)); +- this.worldObj.setBlock(k, i, j, Blocks.air); ++ // CraftBukkit start - Pickup event ++ if (this.worldObj.getWorld() == null || !CraftEventFactory.callEntityChangeBlockEvent(this, this.worldObj.getWorld().getBlockAt(i, j, k), org.bukkit.Material.AIR).isCancelled()) // Cauldron ++ { ++ this.func_146081_a(block); ++ this.setCarryingData(this.worldObj.getBlockMetadata(i, j, k)); ++ this.worldObj.setBlock(i, j, k, Blocks.air); ++ } ++ ++ // CraftBukkit end + } + } + } +@@ -177,8 +189,14 @@ + + if (block.getMaterial() == Material.air && block1.getMaterial() != Material.air && block1.renderAsNormalBlock()) + { +- this.worldObj.setBlock(k, i, j, this.func_146080_bZ(), this.getCarryingData(), 3); +- this.func_146081_a(Blocks.air); ++ // CraftBukkit start - Place event ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this, i, j, k, this.func_146080_bZ(), this.getCarryingData()).isCancelled()) ++ { ++ this.worldObj.setBlock(i, j, k, this.func_146080_bZ(), this.getCarryingData(), 3); ++ this.func_146081_a(Blocks.air); ++ } ++ ++ // CraftBukkit end + } + } + } +@@ -306,8 +324,19 @@ + + if (flag1) + { +- this.setPosition(this.posX, this.posY, this.posZ); ++ // CraftBukkit start - Teleport event ++ EntityTeleportEvent teleport = new EntityTeleportEvent(this.getBukkitEntity(), new Location(this.worldObj.getWorld(), d3, d4, d5), new Location(this.worldObj.getWorld(), this.posX, this.posY, this.posZ)); ++ this.worldObj.getServer().getPluginManager().callEvent(teleport); + ++ if (teleport.isCancelled()) ++ { ++ return false; ++ } ++ ++ Location to = teleport.getTo(); ++ this.setPosition(to.getX(), to.getY(), to.getZ()); ++ // CraftBukkit end ++ + if (this.worldObj.getCollidingBoundingBoxes(this, this.boundingBox).isEmpty() && !this.worldObj.isAnyLiquid(this.boundingBox)) + { + flag = true; diff --git a/patches/net/minecraft/entity/monster/EntityGhast.java.patch b/patches/net/minecraft/entity/monster/EntityGhast.java.patch new file mode 100644 index 0000000..d0662ae --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntityGhast.java.patch @@ -0,0 +1,77 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntityGhast.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntityGhast.java +@@ -18,6 +18,11 @@ + import net.minecraft.world.EnumDifficulty; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.event.entity.EntityTargetEvent; ++// CraftBukkit end ++ + public class EntityGhast extends EntityFlying implements IMob + { + public int courseChangeCooldown; +@@ -117,13 +122,50 @@ + + if (this.targetedEntity != null && this.targetedEntity.isDead) + { +- this.targetedEntity = null; ++ // CraftBukkit start ++ EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), null, EntityTargetEvent.TargetReason.TARGET_DIED); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ if (event.getTarget() == null) ++ { ++ this.targetedEntity = null; ++ } ++ else ++ { ++ this.targetedEntity = ((CraftEntity) event.getTarget()).getHandle(); ++ } ++ } ++ ++ // CraftBukkit end + } + + if (this.targetedEntity == null || this.aggroCooldown-- <= 0) + { +- this.targetedEntity = this.worldObj.getClosestVulnerablePlayerToEntity(this, 100.0D); ++ // CraftBukkit start ++ Entity target = this.worldObj.getClosestVulnerablePlayerToEntity(this, 100.0D); + ++ if (target != null) ++ { ++ EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), target.getBukkitEntity(), EntityTargetEvent.TargetReason.CLOSEST_PLAYER); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ if (event.getTarget() == null) ++ { ++ this.targetedEntity = null; ++ } ++ else ++ { ++ this.targetedEntity = ((CraftEntity) event.getTarget()).getHandle(); ++ } ++ } ++ } ++ ++ // CraftBukkit end ++ + if (this.targetedEntity != null) + { + this.aggroCooldown = 20; +@@ -152,7 +194,8 @@ + { + this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1008, (int)this.posX, (int)this.posY, (int)this.posZ, 0); + EntityLargeFireball entitylargefireball = new EntityLargeFireball(this.worldObj, this, d5, d6, d7); +- entitylargefireball.field_92057_e = this.explosionStrength; ++ // CraftBukkit - set bukkitYield when setting explosionpower ++ entitylargefireball.bukkitYield = entitylargefireball.field_92057_e = this.explosionStrength; + double d8 = 4.0D; + Vec3 vec3 = this.getLook(1.0F); + entitylargefireball.posX = this.posX + vec3.xCoord * d8; diff --git a/patches/net/minecraft/entity/monster/EntityMob.java.patch b/patches/net/minecraft/entity/monster/EntityMob.java.patch new file mode 100644 index 0000000..0cb0f12 --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntityMob.java.patch @@ -0,0 +1,40 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntityMob.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntityMob.java +@@ -1,5 +1,6 @@ + package net.minecraft.entity.monster; + ++import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit + import net.minecraft.enchantment.EnchantmentHelper; + import net.minecraft.entity.Entity; + import net.minecraft.entity.EntityCreature; +@@ -75,7 +76,29 @@ + { + if (entity != this) + { +- this.entityToAttack = entity; ++ // CraftBukkit start - We still need to call events for entities without goals ++ if (entity != this.entityToAttack && (this instanceof EntityBlaze || this instanceof EntityEnderman || this instanceof EntitySpider || this instanceof EntityGiantZombie || this instanceof EntitySilverfish)) ++ { ++ EntityTargetEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTargetEvent(this, entity, EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY); ++ ++ if (!event.isCancelled()) ++ { ++ if (event.getTarget() == null) ++ { ++ this.entityToAttack = null; ++ } ++ else ++ { ++ this.entityToAttack = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); ++ } ++ } ++ } ++ else ++ { ++ this.entityToAttack = entity; ++ } ++ ++ // CraftBukkit end + } + + return true; diff --git a/patches/net/minecraft/entity/monster/EntityPigZombie.java.patch b/patches/net/minecraft/entity/monster/EntityPigZombie.java.patch new file mode 100644 index 0000000..d712abb --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntityPigZombie.java.patch @@ -0,0 +1,42 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntityPigZombie.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntityPigZombie.java +@@ -15,11 +15,13 @@ + import net.minecraft.world.EnumDifficulty; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit ++ + public class EntityPigZombie extends EntityZombie + { + private static final UUID field_110189_bq = UUID.fromString("49455A49-7EC5-45BA-B886-3B90B23A1718"); + private static final AttributeModifier field_110190_br = (new AttributeModifier(field_110189_bq, "Attacking speed boost", 0.45D, 0)).setSaved(false); +- private int angerLevel; ++ public int angerLevel; // CraftBukkit - private -> public + private int randomSoundDelay; + private Entity field_110191_bu; + private static final String __OBFID = "CL_00001693"; +@@ -122,6 +124,24 @@ + + private void becomeAngryAt(Entity p_70835_1_) + { ++ // CraftBukkit start ++ org.bukkit.entity.Entity bukkitTarget = p_70835_1_ == null ? null : p_70835_1_.getBukkitEntity(); ++ EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), bukkitTarget, EntityTargetEvent.TargetReason.PIG_ZOMBIE_TARGET); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ if (event.getTarget() == null) ++ { ++ this.entityToAttack = null; ++ return; ++ } ++ ++ p_70835_1_ = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); ++ // CraftBukkit end + this.entityToAttack = p_70835_1_; + this.angerLevel = 400 + this.rand.nextInt(400); + this.randomSoundDelay = this.rand.nextInt(40); diff --git a/patches/net/minecraft/entity/monster/EntitySilverfish.java.patch b/patches/net/minecraft/entity/monster/EntitySilverfish.java.patch new file mode 100644 index 0000000..59d7184 --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntitySilverfish.java.patch @@ -0,0 +1,39 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntitySilverfish.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntitySilverfish.java +@@ -15,6 +15,8 @@ + import net.minecraft.world.World; + import org.apache.commons.lang3.tuple.ImmutablePair; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntitySilverfish extends EntityMob + { + private int allySummonCooldown; +@@ -132,6 +134,13 @@ + { + if (this.worldObj.getBlock(i + i1, j + l, k + j1) == Blocks.monster_egg) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, i + i1, j + l, k + j1, Blocks.air, 0).isCancelled()) ++ { ++ continue; ++ } ++ ++ // CraftBukkit end + if (!this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")) + { + int k1 = this.worldObj.getBlockMetadata(i + i1, j + l, k + j1); +@@ -168,6 +177,13 @@ + + if (BlockSilverfish.func_150196_a(block)) + { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, i + Facing.offsetsXForSide[l1], j + Facing.offsetsYForSide[l1], k + Facing.offsetsZForSide[l1], Blocks.monster_egg, Block.getIdFromBlock(BlockSilverfish.getBlockById(i1))).isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.worldObj.setBlock(i + Facing.offsetsXForSide[l1], j + Facing.offsetsYForSide[l1], k + Facing.offsetsZForSide[l1], Blocks.monster_egg, BlockSilverfish.func_150195_a(block, i1), 3); + this.spawnExplosionParticle(); + this.setDead(); diff --git a/patches/net/minecraft/entity/monster/EntitySkeleton.java.patch b/patches/net/minecraft/entity/monster/EntitySkeleton.java.patch new file mode 100644 index 0000000..383d7fb --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntitySkeleton.java.patch @@ -0,0 +1,54 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntitySkeleton.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntitySkeleton.java +@@ -36,6 +36,8 @@ + import net.minecraft.world.World; + import net.minecraft.world.WorldProviderHell; + ++import org.bukkit.event.entity.EntityCombustEvent; // CraftBukkit ++ + public class EntitySkeleton extends EntityMob implements IRangedAttackMob + { + private EntityAIArrowAttack aiArrowAttack = new EntityAIArrowAttack(this, 1.0D, 20, 60, 15.0F); +@@ -148,7 +150,16 @@ + + if (flag) + { +- this.setFire(8); ++ // CraftBukkit start ++ EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), 8); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.setFire(event.getDuration()); ++ } ++ ++ // CraftBukkit end + } + } + } +@@ -312,8 +323,23 @@ + entityarrow.setFire(100); + } + ++ // CraftBukkit start ++ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getHeldItem(), entityarrow, 0.8F); ++ ++ if (event.isCancelled()) ++ { ++ event.getProjectile().remove(); ++ return; ++ } ++ ++ if (event.getProjectile() == entityarrow.getBukkitEntity()) ++ { ++ worldObj.spawnEntityInWorld(entityarrow); ++ } ++ ++ // CraftBukkit end + this.playSound("random.bow", 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F)); +- this.worldObj.spawnEntityInWorld(entityarrow); ++ // this.worldObj.spawnEntityInWorld(entityarrow); // CraftBukkit - moved up + } + + public int getSkeletonType() diff --git a/patches/net/minecraft/entity/monster/EntitySlime.java.patch b/patches/net/minecraft/entity/monster/EntitySlime.java.patch new file mode 100644 index 0000000..fb20188 --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntitySlime.java.patch @@ -0,0 +1,94 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntitySlime.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntitySlime.java +@@ -14,12 +14,21 @@ + import net.minecraft.world.biome.BiomeGenBase; + import net.minecraft.world.chunk.Chunk; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityTargetEvent; ++import org.bukkit.event.entity.SlimeSplitEvent; ++import net.minecraft.entity.Entity; ++// CraftBukkit end ++ + public class EntitySlime extends EntityLiving implements IMob + { + public float squishAmount; + public float squishFactor; + public float prevSquishFactor; + private int slimeJumpDelay; ++ private Entity lastTarget; // CraftBukkit + private static final String __OBFID = "CL_00001698"; + + public EntitySlime(World p_i1742_1_) +@@ -37,7 +46,7 @@ + this.dataWatcher.addObject(16, new Byte((byte)1)); + } + +- protected void setSlimeSize(int p_70799_1_) ++ public void setSlimeSize(int p_70799_1_) // CraftBukkit - protected -> public + { + this.dataWatcher.updateObject(16, new Byte((byte)p_70799_1_)); + this.setSize(0.6F * (float)p_70799_1_, 0.6F * (float)p_70799_1_); +@@ -131,8 +140,27 @@ + protected void updateEntityActionState() + { + this.despawnEntity(); +- EntityPlayer entityplayer = this.worldObj.getClosestVulnerablePlayerToEntity(this, 16.0D); ++ // CraftBukkit start ++ Entity entityplayer = this.worldObj.getClosestVulnerablePlayerToEntity(this, 16.0D); // EntityPlayer -> Entity ++ EntityTargetEvent event = null; + ++ if (entityplayer != null && !entityplayer.equals(lastTarget)) ++ { ++ event = CraftEventFactory.callEntityTargetEvent(this, entityplayer, EntityTargetEvent.TargetReason.CLOSEST_PLAYER); ++ } ++ else if (lastTarget != null && entityplayer == null) ++ { ++ event = CraftEventFactory.callEntityTargetEvent(this, entityplayer, EntityTargetEvent.TargetReason.FORGOT_TARGET); ++ } ++ ++ if (event != null && !event.isCancelled()) ++ { ++ entityplayer = event.getTarget() == null ? null : ((CraftEntity) event.getTarget()).getHandle(); ++ } ++ ++ this.lastTarget = entityplayer; ++ // CraftBukkit end ++ + if (entityplayer != null) + { + this.faceEntity(entityplayer, 10.0F, 20.0F); +@@ -190,7 +218,22 @@ + if (!this.worldObj.isRemote && i > 1 && this.getHealth() <= 0.0F) + { + int j = 2 + this.rand.nextInt(3); ++ // CraftBukkit start ++ SlimeSplitEvent event = new SlimeSplitEvent((org.bukkit.entity.Slime) this.getBukkitEntity(), j); ++ this.worldObj.getServer().getPluginManager().callEvent(event); + ++ if (!event.isCancelled() && event.getCount() > 0) ++ { ++ j = event.getCount(); ++ } ++ else ++ { ++ super.setDead(); ++ return; ++ } ++ ++ // CraftBukkit end ++ + for (int k = 0; k < j; ++k) + { + float f = ((float)(k % 2) - 0.5F) * (float)i / 4.0F; +@@ -198,7 +241,7 @@ + EntitySlime entityslime = this.createInstance(); + entityslime.setSlimeSize(i / 2); + entityslime.setLocationAndAngles(this.posX + (double)f, this.posY + 0.5D, this.posZ + (double)f1, this.rand.nextFloat() * 360.0F, 0.0F); +- this.worldObj.spawnEntityInWorld(entityslime); ++ this.worldObj.addEntity(entityslime, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SLIME_SPLIT); // CraftBukkit - SpawnReason + } + } + diff --git a/patches/net/minecraft/entity/monster/EntitySnowman.java.patch b/patches/net/minecraft/entity/monster/EntitySnowman.java.patch new file mode 100644 index 0000000..5aad01e --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntitySnowman.java.patch @@ -0,0 +1,44 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntitySnowman.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntitySnowman.java +@@ -19,6 +19,12 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.util.CraftMagicNumbers; ++import org.bukkit.event.block.EntityBlockFormEvent; ++// CraftBukkit end ++ + public class EntitySnowman extends EntityGolem implements IRangedAttackMob + { + private static final String __OBFID = "CL_00001650"; +@@ -61,7 +67,7 @@ + + if (this.worldObj.getBiomeGenForCoords(i, k).getFloatTemperature(i, j, k) > 1.0F) + { +- this.attackEntityFrom(DamageSource.onFire, 1.0F); ++ this.attackEntityFrom(CraftEventFactory.MELTING, 1.0F); // CraftBukkit - DamageSource.BURN -> CraftEventFactory.MELTING + } + + for (int l = 0; l < 4; ++l) +@@ -72,7 +78,18 @@ + + if (this.worldObj.getBlock(i, j, k).getMaterial() == Material.air && this.worldObj.getBiomeGenForCoords(i, k).getFloatTemperature(i, j, k) < 0.8F && Blocks.snow_layer.canPlaceBlockAt(this.worldObj, i, j, k)) + { +- this.worldObj.setBlock(i, j, k, Blocks.snow_layer); ++ // CraftBukkit start ++ org.bukkit.block.BlockState blockState = this.worldObj.getWorld().getBlockAt(i, j, k).getState(); ++ blockState.setType(CraftMagicNumbers.getMaterial(Blocks.snow_layer)); ++ EntityBlockFormEvent event = new EntityBlockFormEvent(this.getBukkitEntity(), blockState.getBlock(), blockState); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ ++ // CraftBukkit end + } + } + } diff --git a/patches/net/minecraft/entity/monster/EntitySpider.java.patch b/patches/net/minecraft/entity/monster/EntitySpider.java.patch new file mode 100644 index 0000000..19bfbf8 --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntitySpider.java.patch @@ -0,0 +1,38 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntitySpider.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntitySpider.java +@@ -14,6 +14,8 @@ + import net.minecraft.world.EnumDifficulty; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit ++ + public class EntitySpider extends EntityMob + { + private static final String __OBFID = "CL_00001699"; +@@ -88,7 +90,25 @@ + + if (f1 > 0.5F && this.rand.nextInt(100) == 0) + { +- this.entityToAttack = null; ++ // CraftBukkit start ++ EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), null, EntityTargetEvent.TargetReason.FORGOT_TARGET); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ if (event.getTarget() == null) ++ { ++ this.entityToAttack = null; ++ } ++ else ++ { ++ this.entityToAttack = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); ++ } ++ ++ return; ++ } ++ ++ // CraftBukkit end + } + else + { diff --git a/patches/net/minecraft/entity/monster/EntityZombie.java.patch b/patches/net/minecraft/entity/monster/EntityZombie.java.patch new file mode 100644 index 0000000..9c7bbb5 --- /dev/null +++ b/patches/net/minecraft/entity/monster/EntityZombie.java.patch @@ -0,0 +1,109 @@ +--- ../src-base/minecraft/net/minecraft/entity/monster/EntityZombie.java ++++ ../src-work/minecraft/net/minecraft/entity/monster/EntityZombie.java +@@ -46,6 +46,13 @@ + import net.minecraftforge.event.ForgeEventFactory; + import net.minecraftforge.event.entity.living.ZombieEvent.SummonAidEvent; + ++//CraftBukkit start ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.craftbukkit.entity.CraftLivingEntity; ++import org.bukkit.event.entity.EntityCombustByEntityEvent; ++import org.bukkit.event.entity.EntityCombustEvent; ++//CraftBukkit end ++ + public class EntityZombie extends EntityMob + { + protected static final IAttribute field_110186_bp = (new RangedAttribute("zombie.spawnReinforcements", 0.0D, 0.0D, 1.0D)).setDescription("Spawn Reinforcements Chance"); +@@ -56,6 +63,7 @@ + private boolean field_146076_bu = false; + private float field_146074_bv = -1.0F; + private float field_146073_bw; ++ private int lastTick = MinecraftServer.currentTick; // CraftBukkit + private static final String __OBFID = "CL_00001702"; + + public EntityZombie(World p_i1745_1_) +@@ -64,7 +72,12 @@ + this.getNavigator().setBreakDoors(true); + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(2, new EntityAIAttackOnCollide(this, EntityPlayer.class, 1.0D, false)); +- this.tasks.addTask(4, new EntityAIAttackOnCollide(this, EntityVillager.class, 1.0D, true)); ++ ++ if (p_i1745_1_.getSpigotConfig().zombieAggressiveTowardsVillager) // Cauldron ++ { ++ this.tasks.addTask(4, new EntityAIAttackOnCollide(this, EntityVillager.class, 1.0D, true)); // Spigot ++ } ++ + this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D)); + this.tasks.addTask(6, new EntityAIMoveThroughVillage(this, 1.0D, false)); + this.tasks.addTask(7, new EntityAIWander(this, 1.0D)); +@@ -72,7 +85,12 @@ + this.tasks.addTask(8, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, 0, true)); +- this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityVillager.class, 0, false)); ++ ++ if (p_i1745_1_.getSpigotConfig().zombieAggressiveTowardsVillager) // Cauldron ++ { ++ this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityVillager.class, 0, false)); // Spigot ++ } ++ + this.setSize(0.6F, 1.8F); + } + +@@ -204,7 +222,16 @@ + + if (flag) + { +- this.setFire(8); ++ // CraftBukkit start ++ EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), 8); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.setFire(event.getDuration()); ++ } ++ ++ // CraftBukkit end + } + } + } +@@ -272,7 +299,7 @@ + + if (this.worldObj.checkNoEntityCollision(entityzombie.boundingBox) && this.worldObj.getCollidingBoundingBoxes(entityzombie, entityzombie.boundingBox).isEmpty() && !this.worldObj.isAnyLiquid(entityzombie.boundingBox)) + { +- this.worldObj.spawnEntityInWorld(entityzombie); ++ this.worldObj.addEntity(entityzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.REINFORCEMENTS); // CraftBukkit + if (entitylivingbase != null) entityzombie.setAttackTarget(entitylivingbase); + entityzombie.onSpawnWithEgg((IEntityLivingData)null); + this.getEntityAttribute(field_110186_bp).applyModifier(new AttributeModifier("Zombie reinforcement caller charge", -0.05000000074505806D, 0)); +@@ -292,6 +319,11 @@ + if (!this.worldObj.isRemote && this.isConverting()) + { + int i = this.getConversionTimeBoost(); ++ // CraftBukkit start - Use wall time instead of ticks for villager conversion ++ int elapsedTicks = MinecraftServer.currentTick - this.lastTick; ++ this.lastTick = MinecraftServer.currentTick; ++ i *= elapsedTicks; ++ // CraftBukkit end + this.conversionTime -= i; + + if (this.conversionTime <= 0) +@@ -313,7 +345,16 @@ + + if (this.getHeldItem() == null && this.isBurning() && this.rand.nextFloat() < (float)i * 0.3F) + { +- p_70652_1_.setFire(2 * i); ++ // CraftBukkit start ++ EntityCombustByEntityEvent event = new EntityCombustByEntityEvent(this.getBukkitEntity(), p_70652_1_.getBukkitEntity(), 2 * i); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ p_70652_1_.setFire(event.getDuration()); ++ } ++ ++ // CraftBukkit end + } + } + diff --git a/patches/net/minecraft/entity/passive/EntityCow.java.patch b/patches/net/minecraft/entity/passive/EntityCow.java.patch new file mode 100644 index 0000000..2852437 --- /dev/null +++ b/patches/net/minecraft/entity/passive/EntityCow.java.patch @@ -0,0 +1,42 @@ +--- ../src-base/minecraft/net/minecraft/entity/passive/EntityCow.java ++++ ../src-work/minecraft/net/minecraft/entity/passive/EntityCow.java +@@ -17,6 +17,11 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++// CraftBukkit end ++ + public class EntityCow extends EntityAnimal + { + private static final String __OBFID = "CL_00001640"; +@@ -109,15 +114,25 @@ + + if (itemstack != null && itemstack.getItem() == Items.bucket && !p_70085_1_.capabilities.isCreativeMode) + { ++ // CraftBukkit start - Got milk? ++ org.bukkit.Location loc = this.getBukkitEntity().getLocation(); ++ org.bukkit.event.player.PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(p_70085_1_, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), -1, itemstack, Items.milk_bucket); ++ ++ if (event.isCancelled()) ++ { ++ return false; ++ } ++ + if (itemstack.stackSize-- == 1) + { +- p_70085_1_.inventory.setInventorySlotContents(p_70085_1_.inventory.currentItem, new ItemStack(Items.milk_bucket)); ++ p_70085_1_.inventory.setInventorySlotContents(p_70085_1_.inventory.currentItem, CraftItemStack.asNMSCopy(event.getItemStack())); + } + else if (!p_70085_1_.inventory.addItemStackToInventory(new ItemStack(Items.milk_bucket))) + { +- p_70085_1_.dropPlayerItemWithRandomChoice(new ItemStack(Items.milk_bucket, 1, 0), false); ++ p_70085_1_.dropPlayerItemWithRandomChoice(CraftItemStack.asNMSCopy(event.getItemStack()), false); + } + ++ // CraftBukkit end + return true; + } + else diff --git a/patches/net/minecraft/entity/passive/EntityHorse.java.patch b/patches/net/minecraft/entity/passive/EntityHorse.java.patch new file mode 100644 index 0000000..22a4a79 --- /dev/null +++ b/patches/net/minecraft/entity/passive/EntityHorse.java.patch @@ -0,0 +1,134 @@ +--- ../src-base/minecraft/net/minecraft/entity/passive/EntityHorse.java ++++ ../src-work/minecraft/net/minecraft/entity/passive/EntityHorse.java +@@ -50,7 +50,7 @@ + return p_82704_1_ instanceof EntityHorse && ((EntityHorse)p_82704_1_).func_110205_ce(); + } + }; +- private static final IAttribute horseJumpStrength = (new RangedAttribute("horse.jumpStrength", 0.7D, 0.0D, 2.0D)).setDescription("Jump Strength").setShouldWatch(true); ++ public static final IAttribute horseJumpStrength = (new RangedAttribute("horse.jumpStrength", 0.7D, 0.0D, 2.0D)).setDescription("Jump Strength").setShouldWatch(true); // CraftBukkit - private -> public + private static final String[] horseArmorTextures = new String[] {null, "textures/entity/horse/armor/horse_armor_iron.png", "textures/entity/horse/armor/horse_armor_gold.png", "textures/entity/horse/armor/horse_armor_diamond.png"}; + private static final String[] field_110273_bx = new String[] {"", "meo", "goo", "dio"}; + private static final int[] armorValues = new int[] {0, 5, 7, 11}; +@@ -64,7 +64,7 @@ + public int field_110278_bp; + public int field_110279_bq; + protected boolean horseJumping; +- private AnimalChest horseChest; ++ public AnimalChest horseChest; // CraftBukkit - private -> public + private boolean hasReproduced; + protected int temper; + protected float jumpPower; +@@ -78,6 +78,7 @@ + private int field_110285_bP; + private String field_110286_bQ; + private String[] field_110280_bR = new String[3]; ++ public int maxDomestication = 100; // CraftBukkit - store max domestication value + private static final String __OBFID = "CL_00001641"; + + public EntityHorse(World p_i1685_1_) +@@ -403,13 +404,13 @@ + private int func_110225_cC() + { + int i = this.getHorseType(); +- return this.isChested() && (i == 1 || i == 2) ? 17 : 2; ++ return this.isChested() /* && (i == 1 || i == 2) */ ? 17 : 2; // CraftBukkit - Remove type check + } + +- private void func_110226_cD() ++ public void func_110226_cD() // CraftBukkit - private -> public + { + AnimalChest animalchest = this.horseChest; +- this.horseChest = new AnimalChest("HorseChest", this.func_110225_cC()); ++ this.horseChest = new AnimalChest("HorseChest", this.func_110225_cC(), this); // CraftBukkit - add this horse + this.horseChest.func_110133_a(this.getCommandSenderName()); + + if (animalchest != null) +@@ -950,12 +951,25 @@ + { + super.onDeath(p_70645_1_); + ++ /* CraftBukkit start - Handle chest dropping in dropFewItems below + if (!this.worldObj.isRemote) + { + this.dropChestItems(); + } ++ // CraftBukkit end */ + } + ++ // CraftBukkit start - Add method ++ protected void dropFewItems(boolean flag, int i) { ++ super.dropFewItems(flag, i); ++ ++ // Moved from die method above ++ if (!this.worldObj.isRemote) { ++ this.dropChestItems(); ++ } ++ } ++ // CraftBukkit end ++ + public void onLivingUpdate() + { + if (this.rand.nextInt(200) == 0) +@@ -1278,6 +1292,7 @@ + p_70014_1_.setInteger("Temper", this.getTemper()); + p_70014_1_.setBoolean("Tame", this.isTame()); + p_70014_1_.setString("OwnerUUID", this.func_152119_ch()); ++ p_70014_1_.setInteger("Bukkit.MaxDomestication", this.maxDomestication); // CraftBukkit + + if (this.isChested()) + { +@@ -1327,6 +1342,13 @@ + this.func_152120_b(p_70037_1_.getString("OwnerUUID")); + } + ++ // CraftBukkit start ++ if (p_70037_1_.hasKey("Bukkit.MaxDomestication")) ++ { ++ this.maxDomestication = p_70037_1_.getInteger("Bukkit.MaxDomestication"); ++ } ++ ++ // CraftBukkit end + IAttributeInstance iattributeinstance = this.getAttributeMap().getAttributeInstanceByName("Speed"); + + if (iattributeinstance != null) +@@ -1566,24 +1588,33 @@ + { + if (this.isHorseSaddled()) + { ++ // CraftBukkit start - fire HorseJumpEvent, use event power + if (p_110206_1_ < 0) + { + p_110206_1_ = 0; + } +- else +- { +- this.field_110294_bI = true; +- this.makeHorseRear(); +- } + ++ float power; ++ + if (p_110206_1_ >= 90) + { +- this.jumpPower = 1.0F; ++ power = 1.0F; + } + else + { +- this.jumpPower = 0.4F + 0.4F * (float)p_110206_1_ / 90.0F; ++ power = 0.4F + 0.4F * (float)p_110206_1_ / 90.0F; + } ++ ++ org.bukkit.event.entity.HorseJumpEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callHorseJumpEvent(this, power); ++ ++ if (!event.isCancelled()) ++ { ++ this.field_110294_bI = true; ++ this.makeHorseRear(); ++ this.jumpPower = event.getPower(); ++ } ++ ++ // CraftBukkit end + } + } + diff --git a/patches/net/minecraft/entity/passive/EntityMooshroom.java.patch b/patches/net/minecraft/entity/passive/EntityMooshroom.java.patch new file mode 100644 index 0000000..787b01a --- /dev/null +++ b/patches/net/minecraft/entity/passive/EntityMooshroom.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/entity/passive/EntityMooshroom.java ++++ ../src-work/minecraft/net/minecraft/entity/passive/EntityMooshroom.java +@@ -12,6 +12,8 @@ + import net.minecraft.world.World; + import net.minecraftforge.common.IShearable; + ++import org.bukkit.event.player.PlayerShearEntityEvent; // CraftBukkit ++ + public class EntityMooshroom extends EntityCow implements IShearable + { + private static final String __OBFID = "CL_00001645"; diff --git a/patches/net/minecraft/entity/passive/EntityOcelot.java.patch b/patches/net/minecraft/entity/passive/EntityOcelot.java.patch new file mode 100644 index 0000000..4f679f9 --- /dev/null +++ b/patches/net/minecraft/entity/passive/EntityOcelot.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/entity/passive/EntityOcelot.java ++++ ../src-work/minecraft/net/minecraft/entity/passive/EntityOcelot.java +@@ -89,7 +89,7 @@ + + protected boolean canDespawn() + { +- return !this.isTamed() && this.ticksExisted > 2400; ++ return !this.isTamed(); // CraftBukkit + } + + public boolean isAIEnabled() +@@ -188,7 +188,8 @@ + + if (!this.worldObj.isRemote) + { +- if (this.rand.nextInt(3) == 0) ++ // CraftBukkit - added event call and isCancelled check ++ if (this.rand.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, p_70085_1_).isCancelled()) + { + this.setTamed(true); + this.setTameSkin(1 + this.worldObj.rand.nextInt(3)); diff --git a/patches/net/minecraft/entity/passive/EntityPig.java.patch b/patches/net/minecraft/entity/passive/EntityPig.java.patch new file mode 100644 index 0000000..f0e0780 --- /dev/null +++ b/patches/net/minecraft/entity/passive/EntityPig.java.patch @@ -0,0 +1,36 @@ +--- ../src-base/minecraft/net/minecraft/entity/passive/EntityPig.java ++++ ../src-work/minecraft/net/minecraft/entity/passive/EntityPig.java +@@ -22,6 +22,8 @@ + import net.minecraft.stats.AchievementList; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntityPig extends EntityAnimal + { + private final EntityAIControlledByPlayer aiControlledByPlayer; +@@ -171,9 +173,23 @@ + if (!this.worldObj.isRemote) + { + EntityPigZombie entitypigzombie = new EntityPigZombie(this.worldObj); ++ ++ // Cauldron start ++ if (p_70077_1_ != null) ++ { ++ // CraftBukkit start ++ if (CraftEventFactory.callPigZapEvent(this, p_70077_1_, entitypigzombie).isCancelled()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end ++ } ++ // Cauldron end + entitypigzombie.setCurrentItemOrArmor(0, new ItemStack(Items.golden_sword)); + entitypigzombie.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); +- this.worldObj.spawnEntityInWorld(entitypigzombie); ++ // CraftBukkit - added a reason for spawning this creature ++ this.worldObj.addEntity(entitypigzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); + this.setDead(); + } + } diff --git a/patches/net/minecraft/entity/passive/EntitySheep.java.patch b/patches/net/minecraft/entity/passive/EntitySheep.java.patch new file mode 100644 index 0000000..916146e --- /dev/null +++ b/patches/net/minecraft/entity/passive/EntitySheep.java.patch @@ -0,0 +1,42 @@ +--- ../src-base/minecraft/net/minecraft/entity/passive/EntitySheep.java ++++ ../src-work/minecraft/net/minecraft/entity/passive/EntitySheep.java +@@ -32,6 +32,12 @@ + import net.minecraft.world.World; + import net.minecraftforge.common.IShearable; + ++// CraftBukkit start ++import net.minecraft.inventory.InventoryCraftResult; ++import org.bukkit.event.entity.SheepRegrowWoolEvent; ++import org.bukkit.event.player.PlayerShearEntityEvent; ++// CraftBukkit end ++ + public class EntitySheep extends EntityAnimal implements IShearable + { + private final InventoryCrafting field_90016_e = new InventoryCrafting(new Container() +@@ -63,6 +69,7 @@ + this.tasks.addTask(8, new EntityAILookIdle(this)); + this.field_90016_e.setInventorySlotContents(0, new ItemStack(Items.dye, 1, 0)); + this.field_90016_e.setInventorySlotContents(1, new ItemStack(Items.dye, 1, 0)); ++ this.field_90016_e.resultInventory = new InventoryCraftResult(); // CraftBukkit - add result slot for event + } + + protected boolean isAIEnabled() +@@ -231,8 +238,17 @@ + + public void eatGrassBonus() + { +- this.setSheared(false); ++ // CraftBukkit start ++ SheepRegrowWoolEvent event = new SheepRegrowWoolEvent((org.bukkit.entity.Sheep) this.getBukkitEntity()); ++ this.worldObj.getServer().getPluginManager().callEvent(event); + ++ if (!event.isCancelled()) ++ { ++ this.setSheared(false); ++ } ++ ++ // CraftBukkit end ++ + if (this.isChild()) + { + this.addGrowth(60); diff --git a/patches/net/minecraft/entity/passive/EntitySquid.java.patch b/patches/net/minecraft/entity/passive/EntitySquid.java.patch new file mode 100644 index 0000000..2436cc8 --- /dev/null +++ b/patches/net/minecraft/entity/passive/EntitySquid.java.patch @@ -0,0 +1,39 @@ +--- ../src-base/minecraft/net/minecraft/entity/passive/EntitySquid.java ++++ ../src-work/minecraft/net/minecraft/entity/passive/EntitySquid.java +@@ -8,6 +8,8 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.TrigMath; // CraftBukkit ++ + public class EntitySquid extends EntityWaterMob + { + public float squidPitch; +@@ -79,10 +81,12 @@ + } + } + ++ /* CraftBukkit start - Delegate to Entity to use existing inWater value + public boolean isInWater() + { + return this.worldObj.handleMaterialAcceleration(this.boundingBox.expand(0.0D, -0.6000000238418579D, 0.0D), Material.water, this); + } ++ // CraftBukkit end */ + + public void onLivingUpdate() + { +@@ -137,10 +141,12 @@ + } + + f = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); +- this.renderYawOffset += (-((float)Math.atan2(this.motionX, this.motionZ)) * 180.0F / (float)Math.PI - this.renderYawOffset) * 0.1F; ++ // CraftBukkit - Math -> TrigMath ++ this.renderYawOffset += (-((float) TrigMath.atan2(this.motionX, this.motionZ)) * 180.0F / (float)Math.PI - this.renderYawOffset) * 0.1F; + this.rotationYaw = this.renderYawOffset; + this.squidYaw += (float)Math.PI * this.field_70871_bB * 1.5F; +- this.squidPitch += (-((float)Math.atan2((double)f, this.motionY)) * 180.0F / (float)Math.PI - this.squidPitch) * 0.1F; ++ // CraftBukkit - Math -> TrigMath ++ this.squidPitch += (-((float) TrigMath.atan2((double) f, this.motionY)) * 180.0F / (float)Math.PI - this.squidPitch) * 0.1F; + } + else + { diff --git a/patches/net/minecraft/entity/passive/EntityWolf.java.patch b/patches/net/minecraft/entity/passive/EntityWolf.java.patch new file mode 100644 index 0000000..f3419a2 --- /dev/null +++ b/patches/net/minecraft/entity/passive/EntityWolf.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/entity/passive/EntityWolf.java ++++ ../src-work/minecraft/net/minecraft/entity/passive/EntityWolf.java +@@ -139,7 +139,8 @@ + + protected String getLivingSound() + { +- return this.isAngry() ? "mob.wolf.growl" : (this.rand.nextInt(3) == 0 ? (this.isTamed() && this.dataWatcher.getWatchableObjectFloat(18) < 10.0F ? "mob.wolf.whine" : "mob.wolf.panting") : "mob.wolf.bark"); ++ // CraftBukkit - (getFloat(18) < 10) -> (getFloat(18) < this.getMaxHealth() / 2) ++ return this.isAngry() ? "mob.wolf.growl" : (this.rand.nextInt(3) == 0 ? (this.isTamed() && this.dataWatcher.getWatchableObjectFloat(18) < (this.getMaxHealth() / 2) ? "mob.wolf.whine" : "mob.wolf.panting") : "mob.wolf.bark"); + } + + protected String getHurtSound() +@@ -527,7 +528,7 @@ + + protected boolean canDespawn() + { +- return !this.isTamed() && this.ticksExisted > 2400; ++ return !this.isTamed(); // CraftBukkit + } + + public boolean func_142018_a(EntityLivingBase p_142018_1_, EntityLivingBase p_142018_2_) diff --git a/patches/net/minecraft/entity/player/EntityPlayer.java.patch b/patches/net/minecraft/entity/player/EntityPlayer.java.patch new file mode 100644 index 0000000..adf0e59 --- /dev/null +++ b/patches/net/minecraft/entity/player/EntityPlayer.java.patch @@ -0,0 +1,484 @@ +--- ../src-base/minecraft/net/minecraft/entity/player/EntityPlayer.java ++++ ../src-work/minecraft/net/minecraft/entity/player/EntityPlayer.java +@@ -92,6 +92,22 @@ + import net.minecraftforge.event.entity.player.PlayerFlyableFallEvent; + import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; + ++// CraftBukkit start ++import net.minecraft.network.play.server.S2FPacketSetSlot; ++ ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.craftbukkit.entity.CraftItem; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.entity.Player; ++import org.bukkit.event.entity.EntityCombustByEntityEvent; ++import org.bukkit.event.inventory.InventoryCloseEvent; ++import org.bukkit.event.player.PlayerBedEnterEvent; ++import org.bukkit.event.player.PlayerBedLeaveEvent; ++import org.bukkit.event.player.PlayerDropItemEvent; ++import org.bukkit.event.player.PlayerItemConsumeEvent; ++// CraftBukkit end ++ + public abstract class EntityPlayer extends EntityLivingBase implements ICommandSender + { + public static final String PERSISTED_NBT_TAG = "PlayerPersisted"; +@@ -102,7 +118,7 @@ + private InventoryEnderChest theInventoryEnderChest = new InventoryEnderChest(); + public Container inventoryContainer; + public Container openContainer; +- protected FoodStats foodStats = new FoodStats(); ++ protected FoodStats foodStats = new FoodStats(this); // CraftBukkit - add this argument + protected int flyToggleTimer; + public float prevCameraYaw; + public float cameraYaw; +@@ -113,9 +129,19 @@ + public double field_71094_bP; + public double field_71095_bQ; + public double field_71085_bR; +- protected boolean sleeping; ++ // CraftBukkit start ++ public boolean sleeping; // protected -> public ++ public boolean fauxSleeping; ++ public String spawnWorld = ""; ++ ++ @Override ++ public CraftHumanEntity getBukkitEntity() ++ { ++ return (CraftHumanEntity) super.getBukkitEntity(); ++ } ++ // CraftBukkit end + public ChunkCoordinates playerLocation; +- private int sleepTimer; ++ public int sleepTimer; // CraftBukkit - private -> public + public float field_71079_bU; + @SideOnly(Side.CLIENT) + public float field_71082_cx; +@@ -124,6 +150,7 @@ + private boolean spawnForced; + private ChunkCoordinates startMinecartRidingCoordinate; + public PlayerCapabilities capabilities = new PlayerCapabilities(); ++ public int oldLevel = -1; // CraftBukkit + public int experienceLevel; + public int experienceTotal; + public float experience; +@@ -416,6 +443,42 @@ + { + this.updateItemUse(this.itemInUse, 16); + int i = this.itemInUse.stackSize; ++ // CraftBukkit start ++ org.bukkit.inventory.ItemStack craftItem = CraftItemStack.asBukkitCopy(this.itemInUse); ++ PlayerItemConsumeEvent event = new PlayerItemConsumeEvent((Player) this.getBukkitEntity(), craftItem); ++ worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ // Update client ++ if (this instanceof EntityPlayerMP) ++ { ++ ((EntityPlayerMP) this).playerNetServerHandler.sendPacket(new S2FPacketSetSlot((byte) 0, openContainer.getSlotFromInventory( ++ (IInventory) this.inventory, this.inventory.currentItem).slotIndex, this.itemInUse)); ++ // Spigot Start ++ ((EntityPlayerMP) this).getBukkitEntity().updateInventory(); ++ ((EntityPlayerMP) this).getBukkitEntity().updateScaledHealth(); ++ // Spigot End ++ } ++ ++ return; ++ } ++ ++ // Plugin modified the item, process it but don't remove it ++ if (!craftItem.equals(event.getItem())) ++ { ++ CraftItemStack.asNMSCopy(event.getItem()).onFoodEaten(this.worldObj, this); ++ ++ // Update client ++ if (this instanceof EntityPlayerMP) ++ { ++ ((EntityPlayerMP) this).playerNetServerHandler.sendPacket(new S2FPacketSetSlot((byte) 0, openContainer.getSlotFromInventory( ++ (IInventory) this.inventory, this.inventory.currentItem).slotIndex, this.itemInUse)); ++ } ++ ++ return; ++ } ++ // CraftBukkit end + ItemStack itemstack = this.itemInUse.onFoodEaten(this.worldObj, this); + + itemstack = ForgeEventFactory.onItemUseFinish(this, itemInUse, itemInUseCount, itemstack); +@@ -452,6 +515,7 @@ + return this.getHealth() <= 0.0F || this.isPlayerSleeping(); + } + ++ // CraftBukkit - protected -> public + public void closeScreen() + { + this.openContainer = this.inventoryContainer; +@@ -459,23 +523,40 @@ + + public void mountEntity(Entity p_70078_1_) + { ++ // CraftBukkit start - mirror Entity mount changes ++ this.setPassengerOf(p_70078_1_); ++ } ++ ++ public void setPassengerOf(Entity p_70078_1_) ++ { ++ // CraftBukkit end + if (this.ridingEntity != null && p_70078_1_ == null) + { +- if (!this.worldObj.isRemote) +- { +- this.dismountEntity(this.ridingEntity); +- } ++ worldObj.getServer().getPluginManager() ++ .callEvent(new org.spigotmc.event.entity.EntityDismountEvent(this.getBukkitEntity(), this.ridingEntity.getBukkitEntity())); // Spigot ++ // CraftBukkit start - use parent method instead to correctly fire ++ // VehicleExitEvent ++ Entity originalVehicle = this.ridingEntity; ++ // First statement moved down, second statement handled in parent ++ // method. ++ /* ++ * if (!this.world.isStatic) { this.l(this.vehicle); } ++ * ++ * if (this.vehicle != null) { this.vehicle.passenger = null; } ++ * ++ * this.vehicle = null; ++ */ ++ super.setPassengerOf(p_70078_1_); + +- if (this.ridingEntity != null) ++ if (!this.worldObj.isRemote && this.ridingEntity == null) + { +- this.ridingEntity.riddenByEntity = null; ++ this.dismountEntity(originalVehicle); + } +- +- this.ridingEntity = null; ++ // CraftBukkit end + } + else + { +- super.mountEntity(p_70078_1_); ++ super.setPassengerOf(p_70078_1_); // CraftBukkit - call new parent + } + } + +@@ -532,7 +613,8 @@ + + if (this.worldObj.difficultySetting == EnumDifficulty.PEACEFUL && this.getHealth() < this.getMaxHealth() && this.worldObj.getGameRules().getGameRuleBooleanValue("naturalRegeneration") && this.ticksExisted % 20 * 12 == 0) + { +- this.heal(1.0F); ++ // CraftBukkit - added regain reason of "REGEN" for filtering purposes. ++ this.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); + } + + this.inventory.decrementAnimations(); +@@ -554,7 +636,8 @@ + + this.setAIMoveSpeed((float)iattributeinstance.getAttributeValue()); + float f = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); +- float f1 = (float)Math.atan(-this.motionY * 0.20000000298023224D) * 15.0F; ++ // CraftBukkit - Math -> TrigMath ++ float f1 = (float) org.bukkit.craftbukkit.TrigMath.atan(-this.motionY * 0.20000000298023224D) * 15.0F; + + if (f > 0.1F) + { +@@ -589,7 +672,7 @@ + + List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, axisalignedbb); + +- if (list != null) ++ if (list != null && this.canBeCollidedWith()) // Spigot: this.canBeCollidedWith() condition + { + for (int i = 0; i < list.size(); ++i) + { +@@ -687,12 +770,14 @@ + public void addToPlayerScore(Entity p_70084_1_, int p_70084_2_) + { + this.addScore(p_70084_2_); +- Collection collection = this.getWorldScoreboard().func_96520_a(IScoreObjectiveCriteria.totalKillCount); ++ // CraftBukkit - Get our scores instead ++ Collection collection = this.worldObj.getServer().getScoreboardManager().getScoreboardScores(IScoreObjectiveCriteria.totalKillCount, this.getCommandSenderName(), new java.util.ArrayList()); + + if (p_70084_1_ instanceof EntityPlayer) + { + this.addStat(StatList.playerKillsStat, 1); +- collection.addAll(this.getWorldScoreboard().func_96520_a(IScoreObjectiveCriteria.playerKillCount)); ++ // CraftBukkit - Get our scores instead ++ this.worldObj.getServer().getScoreboardManager().getScoreboardScores(IScoreObjectiveCriteria.playerKillCount, this.getCommandSenderName(), collection); + } + else + { +@@ -703,8 +788,7 @@ + + while (iterator.hasNext()) + { +- ScoreObjective scoreobjective = (ScoreObjective)iterator.next(); +- Score score = this.getWorldScoreboard().func_96529_a(this.getCommandSenderName(), scoreobjective); ++ Score score = (Score) iterator.next(); // CraftBukkit - Use our scores instead + score.func_96648_a(); + } + } +@@ -777,6 +861,19 @@ + entityitem.motionZ += Math.sin((double)f1) * (double)f; + } + ++ // CraftBukkit start ++ Player player = (Player) this.getBukkitEntity(); ++ CraftItem drop = new CraftItem(this.worldObj.getServer(), entityitem); ++ PlayerDropItemEvent event = new PlayerDropItemEvent(player, drop); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ player.getInventory().addItem(drop.getItemStack()); ++ return null; ++ } ++ // CraftBukkit end ++ + this.joinEntityItemWithWorld(entityitem); + this.addStat(StatList.dropStat, 1); + return entityitem; +@@ -881,6 +978,15 @@ + this.wakeUpPlayer(true, true, false); + } + ++ // CraftBukkit start ++ this.spawnWorld = p_70037_1_.getString("SpawnWorld"); ++ ++ if ("".equals(spawnWorld)) ++ { ++ this.spawnWorld = this.worldObj.getServer().getWorlds().get(0).getName(); ++ } ++ // CraftBukkit end ++ + if (p_70037_1_.hasKey("SpawnX", 99) && p_70037_1_.hasKey("SpawnY", 99) && p_70037_1_.hasKey("SpawnZ", 99)) + { + this.spawnChunk = new ChunkCoordinates(p_70037_1_.getInteger("SpawnX"), p_70037_1_.getInteger("SpawnY"), p_70037_1_.getInteger("SpawnZ")); +@@ -925,6 +1031,7 @@ + p_70014_1_.setInteger("SpawnY", this.spawnChunk.posY); + p_70014_1_.setInteger("SpawnZ", this.spawnChunk.posZ); + p_70014_1_.setBoolean("SpawnForced", this.spawnForced); ++ p_70014_1_.setString("SpawnWorld", spawnWorld); // CraftBukkit - fixes bed spawns for multiworld worlds + } + + NBTTagList spawnlist = new NBTTagList(); +@@ -1003,7 +1110,7 @@ + { + if (this.worldObj.difficultySetting == EnumDifficulty.PEACEFUL) + { +- p_70097_2_ = 0.0F; ++ return false; // CraftBukkit - p_70097_2_ = 0.0F; -> return false + } + + if (this.worldObj.difficultySetting == EnumDifficulty.EASY) +@@ -1017,7 +1124,7 @@ + } + } + +- if (p_70097_2_ == 0.0F) ++ if (false && p_70097_2_ == 0.0F) // CraftBukkit - Don't filter out 0 damage + { + return false; + } +@@ -1039,9 +1146,40 @@ + + public boolean canAttackPlayer(EntityPlayer p_96122_1_) + { +- Team team = this.getTeam(); +- Team team1 = p_96122_1_.getTeam(); +- return team == null ? true : (!team.isSameTeam(team1) ? true : team.getAllowFriendlyFire()); ++ // CraftBukkit start - Change to check OTHER player's scoreboard team ++ // according to API ++ // To summarize this method's logic, it's "Can parameter hurt this" ++ org.bukkit.scoreboard.Team team; ++ ++ if (p_96122_1_ instanceof EntityPlayerMP) ++ { ++ EntityPlayerMP thatPlayer = (EntityPlayerMP) p_96122_1_; ++ team = thatPlayer.getBukkitEntity().getScoreboard().getPlayerTeam(thatPlayer.getBukkitEntity()); ++ ++ if (team == null || team.allowFriendlyFire()) ++ { ++ return true; ++ } ++ } ++ else ++ { ++ // This should never be called, but is implemented anyway ++ org.bukkit.OfflinePlayer thisPlayer = p_96122_1_.worldObj.getServer().getOfflinePlayer(p_96122_1_.getCommandSenderName()); ++ team = p_96122_1_.worldObj.getServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(thisPlayer); ++ ++ if (team == null || team.allowFriendlyFire()) ++ { ++ return true; ++ } ++ } ++ ++ if (this instanceof EntityPlayerMP) ++ { ++ return !team.hasPlayer(((EntityPlayerMP) this).getBukkitEntity()); ++ } ++ ++ return !team.hasPlayer(this.worldObj.getServer().getOfflinePlayer(this.getCommandSenderName())); ++ // CraftBukkit end + } + + protected void damageArmor(float p_70675_1_) +@@ -1073,19 +1211,34 @@ + return (float)i / (float)this.inventory.armorInventory.length; + } + ++ // Cauldron start - vanilla compatibility + protected void damageEntity(DamageSource p_70665_1_, float p_70665_2_) + { ++ this.damageEntity_CB(p_70665_1_, p_70665_2_); ++ } ++ // Cauldron end ++ ++ // CraftBukkit start ++ protected boolean damageEntity_CB(DamageSource p_70665_1_, float p_70665_2_) // void ++ // -> ++ // boolean ++ { ++ if (true) ++ { ++ return super.damageEntity_CB(p_70665_1_, p_70665_2_); ++ } ++ // CraftBukkit end + if (!this.isEntityInvulnerable()) + { + p_70665_2_ = ForgeHooks.onLivingHurt(this, p_70665_1_, p_70665_2_); +- if (p_70665_2_ <= 0) return; ++ if (p_70665_2_ <= 0) return false; + if (!p_70665_1_.isUnblockable() && this.isBlocking() && p_70665_2_ > 0.0F) + { + p_70665_2_ = (1.0F + p_70665_2_) * 0.5F; + } + + p_70665_2_ = ArmorProperties.ApplyArmor(this, inventory.armorInventory, p_70665_1_, p_70665_2_); +- if (p_70665_2_ <= 0) return; ++ if (p_70665_2_ <= 0) return false; + p_70665_2_ = this.applyPotionDamageCalculations(p_70665_1_, p_70665_2_); + float f1 = p_70665_2_; + p_70665_2_ = Math.max(p_70665_2_ - this.getAbsorptionAmount(), 0.0F); +@@ -1099,6 +1252,7 @@ + this.func_110142_aN().func_94547_a(p_70665_1_, f2, p_70665_2_); + } + } ++ return false; + } + + public void func_146101_a(TileEntityFurnace p_146101_1_) {} +@@ -1134,7 +1288,8 @@ + + if (itemstack.interactWithEntity(this, (EntityLivingBase)p_70998_1_)) + { +- if (itemstack.stackSize <= 0 && !this.capabilities.isCreativeMode) ++ // CraftBukkit - bypass infinite items; <= 0 -> == 0 ++ if (itemstack.stackSize == 0 && !this.capabilities.isCreativeMode) + { + this.destroyCurrentEquippedItem(); + } +@@ -1281,7 +1436,8 @@ + { + itemstack.hitEntity((EntityLivingBase)object, this); + +- if (itemstack.stackSize <= 0) ++ // CraftBukkit - bypass infinite items; <= 0 -> == 0 ++ if (itemstack.stackSize == 0) + { + this.destroyCurrentEquippedItem(); + } +@@ -1293,7 +1449,17 @@ + + if (j > 0) + { +- p_71059_1_.setFire(j * 4); ++ // CraftBukkit start - Call a combust event when ++ // somebody hits with a fire enchanted item ++ EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), p_71059_1_.getBukkitEntity(), ++ j * 4); ++ org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); ++ ++ if (!combustEvent.isCancelled()) ++ { ++ p_71059_1_.setFire(combustEvent.getDuration()); ++ } ++ // CraftBukkit end + } + } + +@@ -1322,6 +1488,10 @@ + + if (this.openContainer != null) + { ++ // CraftBukkit start ++ InventoryCloseEvent event = new InventoryCloseEvent(this.openContainer.getBukkitView()); ++ if (this.openContainer.getBukkitView() != null) Bukkit.getServer().getPluginManager().callEvent(event); // Cauldron - allow vanilla mods to bypass ++ // CraftBukkit end + this.openContainer.onContainerClosed(this); + } + } +@@ -1381,6 +1551,20 @@ + this.mountEntity((Entity)null); + } + ++ // CraftBukkit start ++ if (this.getBukkitEntity() instanceof Player) ++ { ++ Player player = (Player) this.getBukkitEntity(); ++ org.bukkit.block.Block bed = this.worldObj.getWorld().getBlockAt(p_71018_1_, p_71018_2_, p_71018_3_); ++ PlayerBedEnterEvent cbEvent = new PlayerBedEnterEvent(player, bed); ++ this.worldObj.getServer().getPluginManager().callEvent(cbEvent); ++ ++ if (cbEvent.isCancelled()) ++ { ++ return EntityPlayer.EnumStatus.OTHER_PROBLEM; ++ } ++ } ++ // CraftBukkit end + this.setSize(0.2F, 0.2F); + this.yOffset = 0.2F; + +@@ -1476,6 +1660,26 @@ + this.worldObj.updateAllPlayersSleepingFlag(); + } + ++ // CraftBukkit start ++ if (this.getBukkitEntity() instanceof Player) ++ { ++ Player player = (Player) this.getBukkitEntity(); ++ org.bukkit.block.Block bed; ++ ++ if (chunkcoordinates != null) ++ { ++ bed = this.worldObj.getWorld().getBlockAt(chunkcoordinates.posX, chunkcoordinates.posY, chunkcoordinates.posZ); ++ } ++ else ++ { ++ bed = this.worldObj.getWorld().getBlockAt(player.getLocation()); ++ } ++ ++ PlayerBedLeaveEvent event = new PlayerBedLeaveEvent(player, bed); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ } ++ // CraftBukkit end ++ + if (p_70999_1_) + { + this.sleepTimer = 0; +@@ -1606,11 +1810,13 @@ + { + this.spawnChunk = new ChunkCoordinates(p_71063_1_); + this.spawnForced = p_71063_2_; ++ this.spawnWorld = this.worldObj.worldInfo.getWorldName(); // CraftBukkit + } + else + { + this.spawnChunk = null; + this.spawnForced = false; ++ this.spawnWorld = ""; // CraftBukkit + } + } + diff --git a/patches/net/minecraft/entity/player/EntityPlayerMP.java.patch b/patches/net/minecraft/entity/player/EntityPlayerMP.java.patch new file mode 100644 index 0000000..5f889d7 --- /dev/null +++ b/patches/net/minecraft/entity/player/EntityPlayerMP.java.patch @@ -0,0 +1,822 @@ +--- ../src-base/minecraft/net/minecraft/entity/player/EntityPlayerMP.java ++++ ../src-work/minecraft/net/minecraft/entity/player/EntityPlayerMP.java +@@ -2,6 +2,7 @@ + + import com.google.common.collect.Sets; + import com.mojang.authlib.GameProfile; ++ + import io.netty.buffer.Unpooled; + import java.io.IOException; + import java.util.ArrayList; +@@ -83,6 +84,7 @@ + import net.minecraft.tileentity.TileEntityFurnace; + import net.minecraft.tileentity.TileEntityHopper; + import net.minecraft.tileentity.TileEntitySign; ++import net.minecraft.util.ChatComponentTranslation; + import net.minecraft.util.ChunkCoordinates; + import net.minecraft.util.DamageSource; + import net.minecraft.util.EntityDamageSource; +@@ -106,24 +108,38 @@ + import net.minecraftforge.event.entity.player.PlayerDropsEvent; + import net.minecraftforge.event.world.ChunkWatchEvent; + ++// CraftBukkit start ++import net.minecraft.util.CombatTracker; ++import net.minecraft.util.FoodStats; ++import net.minecraft.world.World; ++import org.bukkit.Bukkit; ++import org.bukkit.WeatherType; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.inventory.InventoryType; ++import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; ++// CraftBukkit end ++ + public class EntityPlayerMP extends EntityPlayer implements ICrafting + { + private static final Logger logger = LogManager.getLogger(); +- private String translator = "en_US"; ++ public String translator = "en_US"; // CraftBukkit - private -> public + public NetHandlerPlayServer playerNetServerHandler; + public final MinecraftServer mcServer; + public final ItemInWorldManager theItemInWorldManager; + public double managedPosX; + public double managedPosZ; + public final List loadedChunks = new LinkedList(); +- private final List destroyedItemsNetCache = new LinkedList(); ++ public final List destroyedItemsNetCache = new LinkedList(); + private final StatisticsFile field_147103_bO; + private float field_130068_bO = Float.MIN_VALUE; + private float lastHealth = -1.0E8F; + private int lastFoodLevel = -99999999; + private boolean wasHungry = true; +- private int lastExperience = -99999999; +- private int field_147101_bU = 60; ++ public int lastExperience = -99999999; // CraftBukkit - private -> public ++ public int field_147101_bU = 60; // CraftBukkit - private -> public + private EntityPlayer.EnumChatVisibility chatVisibility; + private boolean chatColours = true; + private long field_143005_bX = System.currentTimeMillis(); +@@ -131,6 +147,39 @@ + public boolean isChangingQuantityOnly; + public int ping; + public boolean playerConqueredTheEnd; ++ // CraftBukkit start ++ public String displayName; ++ public String listName; ++ public org.bukkit.Location compassTarget; ++ public int newExp = 0; ++ public int newLevel = 0; ++ public int newTotalExp = 0; ++ public boolean keepLevel = false; ++ public double maxHealthCache; ++ // CraftBukkit end ++ // Spigot start ++ public boolean collidesWithEntities = true; ++ ++ @Override ++ ++ /** ++ * Returns true if other Entities should be prevented from moving through this Entity. ++ */ ++ public boolean canBeCollidedWith() ++ { ++ return this.collidesWithEntities && super.canBeCollidedWith(); ++ } ++ ++ @Override ++ ++ /** ++ * Returns true if this entity should push and be pushed by other entities when colliding. ++ */ ++ public boolean canBePushed() ++ { ++ return this.collidesWithEntities && super.canBePushed(); ++ } ++ // Spigot end + private static final String __OBFID = "CL_00001440"; + + public EntityPlayerMP(MinecraftServer p_i45285_1_, WorldServer p_i45285_2_, GameProfile p_i45285_3_, ItemInWorldManager p_i45285_4_) +@@ -153,6 +202,13 @@ + { + this.setPosition(this.posX, this.posY + 1.0D, this.posZ); + } ++ ++ // CraftBukkit start ++ this.displayName = this.getCommandSenderName(); ++ this.listName = this.getCommandSenderName(); ++ // this.canPickUpLoot = true; TODO ++ this.maxHealthCache = this.getMaxHealth(); ++ // CraftBukkit end + } + + public void readEntityFromNBT(NBTTagCompound p_70037_1_) +@@ -170,14 +226,57 @@ + this.theItemInWorldManager.setGameType(WorldSettings.GameType.getByID(p_70037_1_.getInteger("playerGameType"))); + } + } ++ ++ this.getBukkitEntity().readExtraData(p_70037_1_); // CraftBukkit + } + + public void writeEntityToNBT(NBTTagCompound p_70014_1_) + { + super.writeEntityToNBT(p_70014_1_); + p_70014_1_.setInteger("playerGameType", this.theItemInWorldManager.getGameType().getID()); ++ this.getBukkitEntity().setExtraData(p_70014_1_); // CraftBukkit + } + ++ // CraftBukkit start - World fallback code, either respawn location or global spawn ++ ++ /** ++ * Sets the reference to the World object. ++ */ ++ public void setWorld(World world) ++ { ++ super.setWorld(world); ++ ++ if (world == null) ++ { ++ this.isDead = false; ++ ChunkCoordinates position = null; ++ ++ if (this.spawnWorld != null && !this.spawnWorld.equals("")) ++ { ++ CraftWorld cworld = (CraftWorld) Bukkit.getServer().getWorld(this.spawnWorld); ++ ++ if (cworld != null && this.getBedLocation() != null) ++ { ++ world = cworld.getHandle(); ++ position = EntityPlayer.verifyRespawnCoordinates(cworld.getHandle(), this.getBedLocation(), false); ++ } ++ } ++ ++ if (world == null || position == null) ++ { ++ world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle(); ++ position = world.getSpawnPoint(); ++ } ++ ++ this.worldObj = world; ++ this.setPosition(position.posX + 0.5, position.posY, position.posZ + 0.5); ++ } ++ ++ this.dimension = ((WorldServer) this.worldObj).provider.dimensionId; ++ this.theItemInWorldManager.setWorld((WorldServer) world); ++ } ++ // CraftBukkit end ++ + public void addExperienceLevel(int p_82242_1_) + { + super.addExperienceLevel(p_82242_1_); +@@ -240,7 +339,7 @@ + ArrayList arraylist1 = new ArrayList(); + Chunk chunk; + +- while (iterator1.hasNext() && arraylist.size() < S26PacketMapChunkBulk.func_149258_c()) ++ while (iterator1.hasNext() && arraylist.size() < this.worldObj.getSpigotConfig().maxBulkChunk) // Spigot // Cauldron + { + ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair)iterator1.next(); + +@@ -253,8 +352,7 @@ + if (chunk.func_150802_k()) + { + arraylist.add(chunk); +- arraylist1.addAll(((WorldServer)this.worldObj).func_147486_a(chunkcoordintpair.chunkXPos * 16, 0, chunkcoordintpair.chunkZPos * 16, chunkcoordintpair.chunkXPos * 16 + 15, 256, chunkcoordintpair.chunkZPos * 16 + 15)); +- //BugFix: 16 makes it load an extra chunk, which isn't associated with a player, which makes it not unload unless a player walks near it. ++ arraylist1.addAll(chunk.chunkTileEntityMap.values()); // CraftBukkit - Get tile entities directly from the chunk instead of the world + iterator1.remove(); + } + } +@@ -309,9 +407,10 @@ + } + } + ++ // CraftBukkit - Optionally scale health + if (this.getHealth() != this.lastHealth || this.lastFoodLevel != this.foodStats.getFoodLevel() || this.foodStats.getSaturationLevel() == 0.0F != this.wasHungry) + { +- this.playerNetServerHandler.sendPacket(new S06PacketUpdateHealth(this.getHealth(), this.foodStats.getFoodLevel(), this.foodStats.getSaturationLevel())); ++ this.playerNetServerHandler.sendPacket(new S06PacketUpdateHealth(this.getBukkitEntity().getScaledHealth(), this.foodStats.getFoodLevel(), this.foodStats.getSaturationLevel())); + this.lastHealth = this.getHealth(); + this.lastFoodLevel = this.foodStats.getFoodLevel(); + this.wasHungry = this.foodStats.getSaturationLevel() == 0.0F; +@@ -320,16 +419,18 @@ + if (this.getHealth() + this.getAbsorptionAmount() != this.field_130068_bO) + { + this.field_130068_bO = this.getHealth() + this.getAbsorptionAmount(); +- Collection collection = this.getWorldScoreboard().func_96520_a(IScoreObjectiveCriteria.health); +- Iterator iterator = collection.iterator(); ++ // CraftBukkit - Update ALL the scores! ++ this.worldObj.getServer().getScoreboardManager().updateAllScoresForList(IScoreObjectiveCriteria.health, this.getCommandSenderName(), com.google.common.collect.ImmutableList.of(this)); ++ } + +- while (iterator.hasNext()) +- { +- ScoreObjective scoreobjective = (ScoreObjective)iterator.next(); +- this.getWorldScoreboard().func_96529_a(this.getCommandSenderName(), scoreobjective).func_96651_a(Arrays.asList(new EntityPlayer[] {this})); +- } ++ // CraftBukkit start - Force max health updates ++ if (this.maxHealthCache != this.getMaxHealth()) ++ { ++ this.getBukkitEntity().updateScaledHealth(); + } + ++ // CraftBukkit end ++ + if (this.experienceTotal != this.lastExperience) + { + this.lastExperience = this.experienceTotal; +@@ -340,6 +441,20 @@ + { + this.func_147098_j(); + } ++ ++ // CraftBukkit start ++ if (this.oldLevel == -1) ++ { ++ this.oldLevel = this.experienceLevel; ++ } ++ ++ if (this.oldLevel != this.experienceLevel) ++ { ++ CraftEventFactory.callPlayerLevelChangeEvent(this.worldObj.getServer().getPlayer((EntityPlayerMP) this), this.oldLevel, this.experienceLevel); ++ this.oldLevel = this.experienceLevel; ++ } ++ ++ // CraftBukkit end + } + catch (Throwable throwable) + { +@@ -402,34 +517,74 @@ + + public void onDeath(DamageSource p_70645_1_) + { +- if (ForgeHooks.onLivingDeath(this, p_70645_1_)) return; +- this.mcServer.getConfigurationManager().sendChatMsg(this.func_110142_aN().func_151521_b()); ++ // CraftBukkit start ++ if (this.isDead || ForgeHooks.onLivingDeath(this, p_70645_1_)) // Cauldron - call Forge hook ++ { ++ return; ++ } + +- if (!this.worldObj.getGameRules().getGameRuleBooleanValue("keepInventory")) ++ java.util.List loot = new java.util.ArrayList(); ++ boolean keepInventory = this.worldObj.getGameRules().getGameRuleBooleanValue("keepInventory"); ++ ++ if (!keepInventory) + { ++ // Cauldron start - rework CraftBukkit logic to support Forge better + captureDrops = true; + capturedDrops.clear(); +- + this.inventory.dropAllItems(); ++ for (int i = 0; i < capturedDrops.size(); ++i) ++ { ++ if (capturedDrops.get(i) != null) ++ { ++ loot.add(CraftItemStack.asCraftMirror(capturedDrops.get(i).getEntityItem())); ++ } ++ } ++ // Cauldron end ++ } + ++ IChatComponent chatmessage = this.func_110142_aN().func_151521_b(); ++ String deathmessage = chatmessage.getUnformattedText(); ++ org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage); ++ String deathMessage = event.getDeathMessage(); ++ ++ if (deathMessage != null && deathMessage.length() > 0) ++ { ++ if (deathMessage.equals(deathmessage)) ++ { ++ this.mcServer.getConfigurationManager().sendChatMsg(chatmessage); ++ } ++ else ++ { ++ this.mcServer.getConfigurationManager().sendMessage(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(deathMessage)); ++ } ++ } ++ ++ if (!keepInventory) ++ { ++ // Cauldron start - rework CraftBukkit logic to support Forge better ++ this.inventory.clearInventory(null, -1); // CraftBukkit - we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory. + captureDrops = false; +- PlayerDropsEvent event = new PlayerDropsEvent(this, p_70645_1_, capturedDrops, recentlyHit > 0); +- if (!MinecraftForge.EVENT_BUS.post(event)) ++ PlayerDropsEvent forgeEvent = new PlayerDropsEvent(this, p_70645_1_, capturedDrops, recentlyHit > 0); ++ ++ if (!MinecraftForge.EVENT_BUS.post(forgeEvent)) + { + for (EntityItem item : capturedDrops) + { + joinEntityItemWithWorld(item); + } + } ++ // Cauldron end + } + +- Collection collection = this.worldObj.getScoreboard().func_96520_a(IScoreObjectiveCriteria.deathCount); ++ this.closeScreen(); ++ // CraftBukkit end ++ // CraftBukkit - Get our scores instead ++ Collection collection = this.worldObj.getServer().getScoreboardManager().getScoreboardScores(IScoreObjectiveCriteria.deathCount, this.getCommandSenderName(), new java.util.ArrayList()); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) + { +- ScoreObjective scoreobjective = (ScoreObjective)iterator.next(); +- Score score = this.getWorldScoreboard().func_96529_a(this.getCommandSenderName(), scoreobjective); ++ Score score = (Score) iterator.next(); // CraftBukkit - Use our scores instead + score.func_96648_a(); + } + +@@ -495,7 +650,8 @@ + + public boolean canAttackPlayer(EntityPlayer p_96122_1_) + { +- return !this.mcServer.isPVPEnabled() ? false : super.canAttackPlayer(p_96122_1_); ++ // CraftBukkit - this.mcServer.isPVPEnabled() -> this.world.pvpMode ++ return !this.worldObj.pvpMode ? false : super.canAttackPlayer(p_96122_1_); + } + + public void travelToDimension(int p_71027_1_) +@@ -526,7 +682,10 @@ + this.triggerAchievement(AchievementList.portal); + } + +- this.mcServer.getConfigurationManager().transferPlayerToDimension(this, p_71027_1_); ++ // CraftBukkit start ++ TeleportCause cause = (this.dimension == 1 || p_71027_1_ == 1) ? TeleportCause.END_PORTAL : TeleportCause.NETHER_PORTAL; ++ this.mcServer.getConfigurationManager().transferPlayerToDimension(this, p_71027_1_, cause); // Cauldron ++ // CraftBukkit end + this.lastExperience = -1; + this.lastHealth = -1.0F; + this.lastFoodLevel = -1; +@@ -569,6 +728,11 @@ + + public void wakeUpPlayer(boolean p_70999_1_, boolean p_70999_2_, boolean p_70999_3_) + { ++ if (this.fauxSleeping && !this.sleeping) ++ { ++ return; // CraftBukkit - Can't leave bed if not in one! ++ } ++ + if (this.isPlayerSleeping()) + { + this.getServerForPlayer().getEntityTracker().func_151248_b(this, new S0BPacketAnimation(this, 2)); +@@ -584,11 +748,27 @@ + + public void mountEntity(Entity p_70078_1_) + { +- super.mountEntity(p_70078_1_); +- this.playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(0, this, this.ridingEntity)); +- this.playerNetServerHandler.setPlayerLocation(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); ++ // CraftBukkit start ++ this.setPassengerOf(p_70078_1_); + } + ++ public void setPassengerOf(Entity entity) ++ { ++ // mount(null) doesn't really fly for overloaded methods, ++ // so this method is needed ++ Entity currentVehicle = this.ridingEntity; ++ super.setPassengerOf(entity); ++ ++ // Check if the vehicle actually changed. ++ if (currentVehicle != this.ridingEntity) ++ { ++ this.playerNetServerHandler.sendPacket(new S1BPacketEntityAttach(0, this, this.ridingEntity)); ++ this.playerNetServerHandler.setPlayerLocation(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); ++ } ++ ++ // CraftBukkit end ++ } ++ + protected void updateFallState(double p_70064_1_, boolean p_70064_3_) {} + + public void handleFalling(double p_71122_1_, boolean p_71122_3_) +@@ -610,29 +790,64 @@ + this.currentWindowId = this.currentWindowId % 100 + 1; + } + ++ // CraftBukkit start - change signature from void to int ++ public int nextContainerCounter() ++ { ++ this.currentWindowId = this.currentWindowId % 100 + 1; ++ return this.currentWindowId; ++ } ++ // CraftBukkit end ++ + public void displayGUIWorkbench(int p_71058_1_, int p_71058_2_, int p_71058_3_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerWorkbench(this.inventory, this.worldObj, p_71058_1_, p_71058_2_, p_71058_3_)); ++ ++ if (container == null) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 1, "Crafting", 9, true)); +- this.openContainer = new ContainerWorkbench(this.inventory, this.worldObj, p_71058_1_, p_71058_2_, p_71058_3_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void displayGUIEnchantment(int p_71002_1_, int p_71002_2_, int p_71002_3_, String p_71002_4_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerEnchantment(this.inventory, this.worldObj, p_71002_1_, p_71002_2_, p_71002_3_)); ++ ++ if (container == null) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 4, p_71002_4_ == null ? "" : p_71002_4_, 9, p_71002_4_ != null)); +- this.openContainer = new ContainerEnchantment(this.inventory, this.worldObj, p_71002_1_, p_71002_2_, p_71002_3_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void displayGUIAnvil(int p_82244_1_, int p_82244_2_, int p_82244_3_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerRepair(this.inventory, this.worldObj, p_82244_1_, p_82244_2_, p_82244_3_, this)); ++ ++ if (container == null) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 8, "Repairing", 9, true)); +- this.openContainer = new ContainerRepair(this.inventory, this.worldObj, p_82244_1_, p_82244_2_, p_82244_3_, this); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } +@@ -644,71 +859,150 @@ + this.closeScreen(); + } + ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerChest(this.inventory, p_71007_1_)); ++ ++ if (container == null) ++ { ++ p_71007_1_.closeInventory(); // Cauldron - prevent chest from being stuck in open state on clients ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 0, p_71007_1_.getInventoryName(), p_71007_1_.getSizeInventory(), p_71007_1_.hasCustomInventoryName())); +- this.openContainer = new ContainerChest(this.inventory, p_71007_1_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void func_146093_a(TileEntityHopper p_146093_1_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHopper(this.inventory, p_146093_1_)); ++ ++ if (container == null) ++ { ++ p_146093_1_.closeInventory(); // Cauldron - prevent chest from being stuck in open state on clients ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 9, p_146093_1_.getInventoryName(), p_146093_1_.getSizeInventory(), p_146093_1_.hasCustomInventoryName())); +- this.openContainer = new ContainerHopper(this.inventory, p_146093_1_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void displayGUIHopperMinecart(EntityMinecartHopper p_96125_1_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHopper(this.inventory, p_96125_1_)); ++ ++ if (container == null) ++ { ++ p_96125_1_.closeInventory(); // Cauldron - prevent chest from being stuck in open state on clients ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 9, p_96125_1_.getInventoryName(), p_96125_1_.getSizeInventory(), p_96125_1_.hasCustomInventoryName())); +- this.openContainer = new ContainerHopper(this.inventory, p_96125_1_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void func_146101_a(TileEntityFurnace p_146101_1_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerFurnace(this.inventory, p_146101_1_)); ++ ++ if (container == null) ++ { ++ p_146101_1_.closeInventory(); // Cauldron - prevent chests from being stuck in open state on clients ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 2, p_146101_1_.getInventoryName(), p_146101_1_.getSizeInventory(), p_146101_1_.hasCustomInventoryName())); +- this.openContainer = new ContainerFurnace(this.inventory, p_146101_1_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void func_146102_a(TileEntityDispenser p_146102_1_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerDispenser(this.inventory, p_146102_1_)); ++ ++ if (container == null) ++ { ++ p_146102_1_.closeInventory(); // Cauldron - prevent chests from being stuck in open state on clients ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, p_146102_1_ instanceof TileEntityDropper ? 10 : 3, p_146102_1_.getInventoryName(), p_146102_1_.getSizeInventory(), p_146102_1_.hasCustomInventoryName())); +- this.openContainer = new ContainerDispenser(this.inventory, p_146102_1_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void func_146098_a(TileEntityBrewingStand p_146098_1_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerBrewingStand(this.inventory, p_146098_1_)); ++ ++ if (container == null) ++ { ++ p_146098_1_.closeInventory(); // Cauldron - prevent chests from being stuck in open state on clients ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 5, p_146098_1_.getInventoryName(), p_146098_1_.getSizeInventory(), p_146098_1_.hasCustomInventoryName())); +- this.openContainer = new ContainerBrewingStand(this.inventory, p_146098_1_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void func_146104_a(TileEntityBeacon p_146104_1_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerBeacon(this.inventory, p_146104_1_)); ++ ++ if (container == null) ++ { ++ p_146104_1_.closeInventory(); // Cauldron - prevent chests from being stuck in open state on clients ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 7, p_146104_1_.getInventoryName(), p_146104_1_.getSizeInventory(), p_146104_1_.hasCustomInventoryName())); +- this.openContainer = new ContainerBeacon(this.inventory, p_146104_1_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } + + public void displayGUIMerchant(IMerchant p_71030_1_, String p_71030_2_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, p_71030_1_, this.worldObj)); ++ ++ if (container == null) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + this.getNextWindowId(); +- this.openContainer = new ContainerMerchant(this.inventory, p_71030_1_, this.worldObj); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + InventoryMerchant inventorymerchant = ((ContainerMerchant)this.openContainer).getMerchantInventory(); +@@ -725,7 +1019,7 @@ + merchantrecipelist.func_151391_a(packetbuffer); + this.playerNetServerHandler.sendPacket(new S3FPacketCustomPayload("MC|TrList", packetbuffer)); + } +- catch (IOException ioexception) ++ catch (Exception ioexception) // CraftBukkit - IOException -> Exception + { + logger.error("Couldn\'t send trade list", ioexception); + } +@@ -738,6 +1032,17 @@ + + public void displayGUIHorse(EntityHorse p_110298_1_, IInventory p_110298_2_) + { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorseInventory(this.inventory, p_110298_2_, p_110298_1_)); ++ ++ if (container == null) ++ { ++ p_110298_2_.closeInventory(); // Cauldron - prevent chests from being stuck in open state on clients ++ return; ++ } ++ ++ // CraftBukkit end ++ + if (this.openContainer != this.inventoryContainer) + { + this.closeScreen(); +@@ -745,7 +1050,7 @@ + + this.getNextWindowId(); + this.playerNetServerHandler.sendPacket(new S2DPacketOpenWindow(this.currentWindowId, 11, p_110298_2_.getInventoryName(), p_110298_2_.getSizeInventory(), p_110298_2_.hasCustomInventoryName(), p_110298_1_.getEntityId())); +- this.openContainer = new ContainerHorseInventory(this.inventory, p_110298_2_, p_110298_1_); ++ this.openContainer = container; // CraftBukkit - Use container we passed to event + this.openContainer.windowId = this.currentWindowId; + this.openContainer.addCraftingToCrafters(this); + } +@@ -770,6 +1075,15 @@ + { + this.playerNetServerHandler.sendPacket(new S30PacketWindowItems(p_71110_1_.windowId, p_71110_2_)); + this.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(-1, -1, this.inventory.getItemStack())); ++ ++ if (p_71110_1_.getBukkitView() == null) return; // Cauldron - allow vanilla mods to bypass ++ // CraftBukkit start - Send a Set Slot to update the crafting result slot ++ if (java.util.EnumSet.of(InventoryType.CRAFTING, InventoryType.WORKBENCH).contains(p_71110_1_.getBukkitView().getType())) ++ { ++ this.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(p_71110_1_.windowId, 0, p_71110_1_.getSlot(0).getStack())); ++ } ++ ++ // CraftBukkit end + } + + public void sendProgressBarUpdate(Container p_71112_1_, int p_71112_2_, int p_71112_3_) +@@ -779,6 +1093,7 @@ + + public void closeScreen() + { ++ CraftEventFactory.handleInventoryCloseEvent(this); // CraftBukkit + this.playerNetServerHandler.sendPacket(new S2EPacketCloseWindow(this.openContainer.windowId)); + this.closeContainer(); + } +@@ -853,8 +1168,19 @@ + public void setPlayerHealthUpdated() + { + this.lastHealth = -1.0E8F; ++ this.lastExperience = -1; // CraftBukkit - Added to reset + } + ++ // CraftBukkit start - Support multi-line messages ++ public void sendMessage(IChatComponent[] ichatcomponent) ++ { ++ for (IChatComponent component : ichatcomponent) ++ { ++ this.addChatComponentMessage(component); ++ } ++ } ++ // CraftBukkit end ++ + public void addChatComponentMessage(IChatComponent p_146105_1_) + { + this.playerNetServerHandler.sendPacket(new S02PacketChat(p_146105_1_)); +@@ -1037,6 +1363,114 @@ + return this.field_143005_bX; + } + ++ // CraftBukkit start ++ public long timeOffset = 0; ++ public boolean relativeTime = true; ++ ++ public long getPlayerTime() ++ { ++ if (this.relativeTime) ++ { ++ // Adds timeOffset to the current server time. ++ return this.worldObj.getWorldTime() + this.timeOffset; ++ } ++ else ++ { ++ // Adds timeOffset to the beginning of this day. ++ return this.worldObj.getWorldTime() - (this.worldObj.getWorldTime() % 24000) + this.timeOffset; ++ } ++ } ++ ++ public WeatherType weather = null; ++ ++ public WeatherType getPlayerWeather() ++ { ++ return this.weather; ++ } ++ ++ public void setPlayerWeather(WeatherType type, boolean plugin) ++ { ++ if (!plugin && this.weather != null) ++ { ++ return; ++ } ++ ++ if (plugin) ++ { ++ this.weather = type; ++ } ++ ++ if (type == WeatherType.DOWNFALL) ++ { ++ this.playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(2, 0)); ++ // this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, this.world.j(1.0F))); ++ // this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, this.world.h(1.0F))); ++ } ++ else ++ { ++ this.playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(1, 0)); ++ } ++ } ++ ++ public void resetPlayerWeather() ++ { ++ this.weather = null; ++ this.setPlayerWeather(this.worldObj.getWorldInfo().isRaining() ? WeatherType.DOWNFALL : WeatherType.CLEAR, false); ++ } ++ ++ @Override ++ public String toString() ++ { ++ return super.toString() + "(" + this.getCommandSenderName() + " at " + this.posX + "," + this.posY + "," + this.posZ + ")"; ++ } ++ ++ public void reset() ++ { ++ float exp = 0; ++ boolean keepInventory = this.worldObj.getGameRules().getGameRuleBooleanValue("keepInventory"); ++ ++ if (this.keepLevel || keepInventory) ++ { ++ exp = this.experience; ++ this.newTotalExp = this.experienceTotal; ++ this.newLevel = this.experienceLevel; ++ } ++ ++ this.setHealth(this.getMaxHealth()); ++ this.fire = 0; ++ this.fallDistance = 0; ++ this.foodStats = new FoodStats(this); ++ this.experienceLevel = this.newLevel; ++ this.experienceTotal = this.newTotalExp; ++ this.experience = 0; ++ this.deathTime = 0; ++ this.clearActivePotions(); // Should be remapped: removeAllEffects should be remapped to this. ++ super.potionsNeedUpdate = true; // Cauldron - change to super to temporarily workaround remapping bug with SpecialSource ++ this.openContainer = this.inventoryContainer; ++ this.attackingPlayer = null; ++ this.entityLivingToAttack = null; ++ this._combatTracker = new CombatTracker(this); ++ this.lastExperience = -1; ++ ++ if (this.keepLevel || keepInventory) ++ { ++ this.experience = exp; ++ } ++ else ++ { ++ this.addExperience(this.newExp); ++ } ++ ++ this.keepLevel = false; ++ } ++ ++ @Override ++ public CraftPlayer getBukkitEntity() ++ { ++ return (CraftPlayer) super.getBukkitEntity(); ++ } ++ // CraftBukkit end ++ + /* ===================================== FORGE START =====================================*/ + /** + * Returns the default eye height of the player diff --git a/patches/net/minecraft/entity/player/InventoryPlayer.java.patch b/patches/net/minecraft/entity/player/InventoryPlayer.java.patch new file mode 100644 index 0000000..8e73044 --- /dev/null +++ b/patches/net/minecraft/entity/player/InventoryPlayer.java.patch @@ -0,0 +1,140 @@ +--- ../src-base/minecraft/net/minecraft/entity/player/InventoryPlayer.java ++++ ../src-work/minecraft/net/minecraft/entity/player/InventoryPlayer.java +@@ -2,7 +2,9 @@ + + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; ++ + import java.util.concurrent.Callable; ++ + import net.minecraft.block.Block; + import net.minecraft.crash.CrashReport; + import net.minecraft.crash.CrashReportCategory; +@@ -13,7 +15,13 @@ + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.nbt.NBTTagList; + import net.minecraft.util.ReportedException; ++// CraftBukkit start ++import java.util.List; + ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventoryPlayer implements IInventory + { + public ItemStack[] mainInventory = new ItemStack[36]; +@@ -25,7 +33,46 @@ + private ItemStack itemStack; + public boolean inventoryChanged; + private static final String __OBFID = "CL_00001709"; ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private int maxStack = MAX_STACK; + ++ public ItemStack[] getContents() ++ { ++ return this.mainInventory; ++ } ++ ++ public ItemStack[] getArmorContents() ++ { ++ return this.armorInventory; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() ++ { ++ return this.player.getBukkitEntity(); ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end ++ + public InventoryPlayer(EntityPlayer p_i1750_1_) + { + this.player = p_i1750_1_; +@@ -81,6 +128,34 @@ + return -1; + } + ++ // CraftBukkit start - Watch method above! :D ++ public int canHold(ItemStack itemstack) ++ { ++ int remains = itemstack.stackSize; ++ ++ for (int i = 0; i < this.mainInventory.length; ++i) ++ { ++ if (this.mainInventory[i] == null) ++ { ++ return itemstack.stackSize; ++ } ++ ++ // Taken from firstPartial(ItemStack) ++ if (this.mainInventory[i] != null && this.mainInventory[i].getItem() == itemstack.getItem() && this.mainInventory[i].isStackable() && this.mainInventory[i].stackSize < this.mainInventory[i].getMaxStackSize() && this.mainInventory[i].stackSize < this.getInventoryStackLimit() && (!this.mainInventory[i].getHasSubtypes() || this.mainInventory[i].getItemDamage() == itemstack.getItemDamage()) && ItemStack.areItemStackTagsEqual(this.mainInventory[i], itemstack)) ++ { ++ remains -= (this.mainInventory[i].getMaxStackSize() < this.getInventoryStackLimit() ? this.mainInventory[i].getMaxStackSize() : this.getInventoryStackLimit()) - this.mainInventory[i].stackSize; ++ } ++ ++ if (remains <= 0) ++ { ++ return itemstack.stackSize; ++ } ++ } ++ ++ return itemstack.stackSize - remains; ++ } ++ // CraftBukkit end ++ + public int getFirstEmptyStack() + { + for (int i = 0; i < this.mainInventory.length; ++i) +@@ -658,7 +733,7 @@ + if (this.mainInventory[i] != null) + { + this.player.func_146097_a(this.mainInventory[i], true, false); +- this.mainInventory[i] = null; ++ //this.mainInventory[i] = null; // Cauldron - we clear this in EntityPlayerMP.onDeath after PlayerDeathEvent + } + } + +@@ -667,7 +742,7 @@ + if (this.armorInventory[i] != null) + { + this.player.func_146097_a(this.armorInventory[i], true, false); +- this.armorInventory[i] = null; ++ //this.armorInventory[i] = null; // Cauldron - we clear this in EntityPlayerMP.onDeath after PlayerDeathEvent + } + } + } +@@ -684,6 +759,13 @@ + + public ItemStack getItemStack() + { ++ // CraftBukkit start ++ if (this.itemStack != null && this.itemStack.stackSize == 0) ++ { ++ this.setItemStack(null); ++ } ++ ++ // CraftBukkit end + return this.itemStack; + } + diff --git a/patches/net/minecraft/entity/player/PlayerCapabilities.java.patch b/patches/net/minecraft/entity/player/PlayerCapabilities.java.patch new file mode 100644 index 0000000..d67f84c --- /dev/null +++ b/patches/net/minecraft/entity/player/PlayerCapabilities.java.patch @@ -0,0 +1,13 @@ +--- ../src-base/minecraft/net/minecraft/entity/player/PlayerCapabilities.java ++++ ../src-work/minecraft/net/minecraft/entity/player/PlayerCapabilities.java +@@ -11,8 +11,8 @@ + public boolean allowFlying; + public boolean isCreativeMode; + public boolean allowEdit = true; +- private float flySpeed = 0.05F; +- private float walkSpeed = 0.1F; ++ public float flySpeed = 0.05F; // CraftBukkit private -> public ++ public float walkSpeed = 0.1F; // CraftBukkit private -> public + private static final String __OBFID = "CL_00001708"; + + public void writeCapabilitiesToNBT(NBTTagCompound p_75091_1_) diff --git a/patches/net/minecraft/entity/projectile/EntityArrow.java.patch b/patches/net/minecraft/entity/projectile/EntityArrow.java.patch new file mode 100644 index 0000000..c12e33d --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntityArrow.java.patch @@ -0,0 +1,141 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntityArrow.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntityArrow.java +@@ -23,6 +23,13 @@ + import net.minecraft.util.Vec3; + import net.minecraft.world.World; + ++// CraftBukkit start ++import net.minecraft.entity.item.EntityItem; ++import org.bukkit.entity.LivingEntity; ++import org.bukkit.event.entity.EntityCombustByEntityEvent; ++import org.bukkit.event.player.PlayerPickupItemEvent; ++// CraftBukkit end ++ + public class EntityArrow extends Entity implements IProjectile + { + private int field_145791_d = -1; +@@ -30,14 +37,14 @@ + private int field_145789_f = -1; + private Block field_145790_g; + private int inData; +- private boolean inGround; ++ public boolean inGround = false; // Spigot - private -> public + public int canBePickedUp; + public int arrowShake; + public Entity shootingEntity; + private int ticksInGround; + private int ticksInAir; + private double damage = 2.0D; +- private int knockbackStrength; ++ public int knockbackStrength; // CraftBukkit - private -> public + private static final String __OBFID = "CL_00001715"; + + public EntityArrow(World p_i1753_1_) +@@ -61,6 +68,7 @@ + super(p_i1755_1_); + this.renderDistanceWeight = 10.0D; + this.shootingEntity = p_i1755_2_; ++ this.projectileSource = (LivingEntity) p_i1755_2_.getBukkitEntity(); // CraftBukkit + + if (p_i1755_2_ instanceof EntityPlayer) + { +@@ -91,6 +99,7 @@ + super(p_i1756_1_); + this.renderDistanceWeight = 10.0D; + this.shootingEntity = p_i1756_2_; ++ this.projectileSource = (LivingEntity) p_i1756_2_.getBukkitEntity(); // CraftBukkit + + if (p_i1756_2_ instanceof EntityPlayer) + { +@@ -199,7 +208,7 @@ + { + ++this.ticksInGround; + +- if (this.ticksInGround == 1200) ++ if (this.ticksInGround == worldObj.getSpigotConfig().arrowDespawnRate) // Spigot // Cauldron + { + this.setDead(); + } +@@ -246,7 +255,7 @@ + + if (movingobjectposition1 != null) + { +- double d1 = vec31.distanceTo(movingobjectposition1.hitVec); ++ double d1 = vec31.squareDistanceTo(movingobjectposition1.hitVec); // CraftBukkit - distance efficiency + + if (d1 < d0 || d0 == 0.0D) + { +@@ -277,6 +286,8 @@ + + if (movingobjectposition != null) + { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); // CraftBukkit - Call event ++ + if (movingobjectposition.entityHit != null) + { + f2 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); +@@ -298,13 +309,23 @@ + damagesource = DamageSource.causeArrowDamage(this, this.shootingEntity); + } + +- if (this.isBurning() && !(movingobjectposition.entityHit instanceof EntityEnderman)) ++ // CraftBukkit start - Moved damage call ++ if (movingobjectposition.entityHit.attackEntityFrom(damagesource, k)) + { +- movingobjectposition.entityHit.setFire(5); +- } ++ if (this.isBurning() && !(movingobjectposition.entityHit instanceof EntityEnderman) && (!(movingobjectposition.entityHit instanceof EntityPlayerMP) || !(this.shootingEntity instanceof EntityPlayerMP) || this.worldObj.pvpMode)) // CraftBukkit - abide by pvp setting if destination is a player ++ { ++ EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 5); ++ org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); + +- if (movingobjectposition.entityHit.attackEntityFrom(damagesource, (float)k)) +- { ++ if (!combustEvent.isCancelled()) ++ { ++ movingobjectposition.entityHit.setFire(combustEvent.getDuration()); ++ } ++ ++ // CraftBukkit end ++ } ++ ++ // if (movingobjectposition.entityHit.attackEntityFrom(damagesource, (float)k)) { // CraftBukkit - moved up + if (movingobjectposition.entityHit instanceof EntityLivingBase) + { + EntityLivingBase entitylivingbase = (EntityLivingBase)movingobjectposition.entityHit; +@@ -487,6 +508,23 @@ + { + if (!this.worldObj.isRemote && this.inGround && this.arrowShake <= 0) + { ++ // CraftBukkit start ++ ItemStack itemstack = new ItemStack(Items.arrow); ++ ++ if (this.canBePickedUp == 1 && p_70100_1_.inventory.canHold(itemstack) > 0) ++ { ++ EntityItem item = new EntityItem(this.worldObj, this.posX, this.posY, this.posZ, itemstack); ++ PlayerPickupItemEvent event = new PlayerPickupItemEvent((org.bukkit.entity.Player) p_70100_1_.getBukkitEntity(), new org.bukkit.craftbukkit.entity.CraftItem(this.worldObj.getServer(), this, item), 0); ++ // event.setCancelled(!entityplayer.canPickUpLoot); TODO ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ } ++ ++ // CraftBukkit end + boolean flag = this.canBePickedUp == 1 || this.canBePickedUp == 2 && p_70100_1_.capabilities.isCreativeMode; + + if (this.canBePickedUp == 1 && !p_70100_1_.inventory.addItemStackToInventory(new ItemStack(Items.arrow, 1))) +@@ -553,4 +591,11 @@ + byte b0 = this.dataWatcher.getWatchableObjectByte(16); + return (b0 & 1) != 0; + } ++ ++ // CraftBukkit start ++ public boolean isInGround() ++ { ++ return inGround; ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/entity/projectile/EntityEgg.java.patch b/patches/net/minecraft/entity/projectile/EntityEgg.java.patch new file mode 100644 index 0000000..a4d1760 --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntityEgg.java.patch @@ -0,0 +1,73 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntityEgg.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntityEgg.java +@@ -6,6 +6,15 @@ + import net.minecraft.util.MovingObjectPosition; + import net.minecraft.world.World; + ++// CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import org.bukkit.entity.Ageable; ++import org.bukkit.entity.EntityType; ++import org.bukkit.entity.Player; ++import org.bukkit.event.player.PlayerEggThrowEvent; ++import net.minecraft.entity.Entity; ++// CraftBukkit end ++ + public class EntityEgg extends EntityThrowable + { + private static final String __OBFID = "CL_00001724"; +@@ -32,24 +41,43 @@ + p_70184_1_.entityHit.attackEntityFrom(DamageSource.causeThrownDamage(this, this.getThrower()), 0.0F); + } + +- if (!this.worldObj.isRemote && this.rand.nextInt(8) == 0) ++ // CraftBukkit start ++ boolean hatching = !this.worldObj.isRemote && this.rand.nextInt(8) == 0; ++ int numHatching = (this.rand.nextInt(32) == 0) ? 4 : 1; ++ ++ if (!hatching) + { +- byte b0 = 1; ++ numHatching = 0; ++ } + +- if (this.rand.nextInt(32) == 0) +- { +- b0 = 4; +- } ++ EntityType hatchingType = EntityType.CHICKEN; ++ Entity shooter = this.getThrower(); + +- for (int i = 0; i < b0; ++i) ++ if (shooter instanceof EntityPlayerMP) ++ { ++ Player player = (shooter == null) ? null : (Player) shooter.getBukkitEntity(); ++ PlayerEggThrowEvent event = new PlayerEggThrowEvent(player, (org.bukkit.entity.Egg) this.getBukkitEntity(), hatching, (byte) numHatching, hatchingType); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ hatching = event.isHatching(); ++ numHatching = event.getNumHatches(); ++ hatchingType = event.getHatchingType(); ++ } ++ ++ if (hatching) ++ { ++ for (int k = 0; k < numHatching; k++) + { +- EntityChicken entitychicken = new EntityChicken(this.worldObj); +- entitychicken.setGrowingAge(-24000); +- entitychicken.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, 0.0F); +- this.worldObj.spawnEntityInWorld(entitychicken); ++ org.bukkit.entity.Entity entity = worldObj.getWorld().spawn(new org.bukkit.Location(worldObj.getWorld(), this.posX, this.posY, this.posZ, this.rotationYaw, 0.0F), hatchingType.getEntityClass(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG); ++ ++ if (entity instanceof Ageable) ++ { ++ ((Ageable) entity).setBaby(); ++ } + } + } + ++ // CraftBukkit end ++ + for (int j = 0; j < 8; ++j) + { + this.worldObj.spawnParticle("snowballpoof", this.posX, this.posY, this.posZ, 0.0D, 0.0D, 0.0D); diff --git a/patches/net/minecraft/entity/projectile/EntityFireball.java.patch b/patches/net/minecraft/entity/projectile/EntityFireball.java.patch new file mode 100644 index 0000000..4ea87ad --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntityFireball.java.patch @@ -0,0 +1,122 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntityFireball.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntityFireball.java +@@ -3,6 +3,7 @@ + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; + import java.util.List; ++ + import net.minecraft.block.Block; + import net.minecraft.entity.Entity; + import net.minecraft.entity.EntityLivingBase; +@@ -15,6 +16,8 @@ + import net.minecraft.util.Vec3; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public abstract class EntityFireball extends Entity + { + private int field_145795_e = -1; +@@ -28,6 +31,8 @@ + public double accelerationX; + public double accelerationY; + public double accelerationZ; ++ public float bukkitYield = 1; // CraftBukkit ++ public boolean isIncendiary = true; // CraftBukkit + private static final String __OBFID = "CL_00001717"; + + public EntityFireball(World p_i1759_1_) +@@ -62,11 +67,19 @@ + { + super(p_i1761_1_); + this.shootingEntity = p_i1761_2_; ++ this.projectileSource = (org.bukkit.entity.LivingEntity) p_i1761_2_.getBukkitEntity(); // CraftBukkit + this.setSize(1.0F, 1.0F); + this.setLocationAndAngles(p_i1761_2_.posX, p_i1761_2_.posY, p_i1761_2_.posZ, p_i1761_2_.rotationYaw, p_i1761_2_.rotationPitch); + this.setPosition(this.posX, this.posY, this.posZ); + this.yOffset = 0.0F; + this.motionX = this.motionY = this.motionZ = 0.0D; ++ // CraftBukkit start - Added setDirection method ++ this.setDirection(p_i1761_3_, p_i1761_5_, p_i1761_7_); ++ } ++ ++ public void setDirection(double p_i1761_3_, double p_i1761_5_, double p_i1761_7_) ++ { ++ // CraftBukkit end + p_i1761_3_ += this.rand.nextGaussian() * 0.4D; + p_i1761_5_ += this.rand.nextGaussian() * 0.4D; + p_i1761_7_ += this.rand.nextGaussian() * 0.4D; +@@ -140,7 +153,7 @@ + + if (movingobjectposition1 != null) + { +- double d1 = vec3.distanceTo(movingobjectposition1.hitVec); ++ double d1 = vec3.squareDistanceTo(movingobjectposition1.hitVec); // CraftBukkit - distance efficiency + + if (d1 < d0 || d0 == 0.0D) + { +@@ -159,6 +172,14 @@ + if (movingobjectposition != null) + { + this.onImpact(movingobjectposition); ++ ++ // CraftBukkit start ++ if (this.isDead) ++ { ++ CraftEventFactory.callProjectileHitEvent(this); ++ } ++ ++ // CraftBukkit end + } + + this.posX += this.motionX; +@@ -227,6 +248,8 @@ + p_70014_1_.setShort("zTile", (short)this.field_145794_g); + p_70014_1_.setByte("inTile", (byte)Block.getIdFromBlock(this.field_145796_h)); + p_70014_1_.setByte("inGround", (byte)(this.inGround ? 1 : 0)); ++ // CraftBukkit - Fix direction being mismapped to invalid variables ++ p_70014_1_.setTag("power", this.newDoubleNBTList(new double[] { this.accelerationX, this.accelerationY, this.accelerationZ})); + p_70014_1_.setTag("direction", this.newDoubleNBTList(new double[] {this.motionX, this.motionY, this.motionZ})); + } + +@@ -238,8 +261,17 @@ + this.field_145796_h = Block.getBlockById(p_70037_1_.getByte("inTile") & 255); + this.inGround = p_70037_1_.getByte("inGround") == 1; + +- if (p_70037_1_.hasKey("direction", 9)) ++ // CraftBukkit start - direction -> power ++ if (p_70037_1_.hasKey("power", 9)) + { ++ NBTTagList nbttaglist = p_70037_1_.getTagList("power", 6); ++ this.accelerationX = nbttaglist.func_150309_d(0); ++ this.accelerationY = nbttaglist.func_150309_d(1); ++ this.accelerationZ = nbttaglist.func_150309_d(2); ++ // CraftBukkit end ++ } ++ else if (p_70037_1_.hasKey("direction", 9)) ++ { + NBTTagList nbttaglist = p_70037_1_.getTagList("direction", 6); + this.motionX = nbttaglist.func_150309_d(0); + this.motionY = nbttaglist.func_150309_d(1); +@@ -273,6 +305,13 @@ + + if (p_70097_1_.getEntity() != null) + { ++ // CraftBukkit start ++ if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, p_70097_1_, p_70097_2_)) ++ { ++ return false; ++ } ++ ++ // CraftBukkit end + Vec3 vec3 = p_70097_1_.getEntity().getLookVec(); + + if (vec3 != null) +@@ -288,6 +327,7 @@ + if (p_70097_1_.getEntity() instanceof EntityLivingBase) + { + this.shootingEntity = (EntityLivingBase)p_70097_1_.getEntity(); ++ this.projectileSource = (org.bukkit.projectiles.ProjectileSource) this.shootingEntity.getBukkitEntity(); // CraftBukkit + } + + return true; diff --git a/patches/net/minecraft/entity/projectile/EntityFishHook.java.patch b/patches/net/minecraft/entity/projectile/EntityFishHook.java.patch new file mode 100644 index 0000000..09da7c4 --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntityFishHook.java.patch @@ -0,0 +1,119 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntityFishHook.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntityFishHook.java +@@ -27,6 +27,12 @@ + import net.minecraft.world.World; + import net.minecraft.world.WorldServer; + ++// CraftBukkit start ++import org.bukkit.entity.Player; ++import org.bukkit.entity.Fish; ++import org.bukkit.event.player.PlayerFishEvent; ++// CraftBukkit end ++ + public class EntityFishHook extends Entity + { + public static final List field_146039_d = Arrays.asList(new WeightedRandomFishable[] {(new WeightedRandomFishable(new ItemStack(Items.leather_boots), 10)).func_150709_a(0.9F), new WeightedRandomFishable(new ItemStack(Items.leather), 10), new WeightedRandomFishable(new ItemStack(Items.bone), 10), new WeightedRandomFishable(new ItemStack(Items.potionitem), 10), new WeightedRandomFishable(new ItemStack(Items.string), 5), (new WeightedRandomFishable(new ItemStack(Items.fishing_rod), 2)).func_150709_a(0.9F), new WeightedRandomFishable(new ItemStack(Items.bowl), 10), new WeightedRandomFishable(new ItemStack(Items.stick), 5), new WeightedRandomFishable(new ItemStack(Items.dye, 10, 0), 1), new WeightedRandomFishable(new ItemStack(Blocks.tripwire_hook), 10), new WeightedRandomFishable(new ItemStack(Items.rotten_flesh), 10)}); +@@ -258,7 +264,7 @@ + + if (movingobjectposition1 != null) + { +- d2 = vec31.distanceTo(movingobjectposition1.hitVec); ++ d2 = vec31.squareDistanceTo(movingobjectposition1.hitVec); // CraftBukkit - distance efficiency + + if (d2 < d0 || d0 == 0.0D) + { +@@ -276,6 +282,8 @@ + + if (movingobjectposition != null) + { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); // Craftbukkit - Call event ++ + if (movingobjectposition.entityHit != null) + { + if (movingobjectposition.entityHit.attackEntityFrom(DamageSource.causeThrownDamage(this, this.field_146042_b), 0.0F)) +@@ -509,6 +517,18 @@ + + if (this.field_146043_c != null) + { ++ // CraftBukkit start ++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.field_146042_b.getBukkitEntity(), this.field_146043_c.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY); ++ this.worldObj.getServer().getPluginManager().callEvent(playerFishEvent); ++ ++ if (playerFishEvent.isCancelled()) ++ { ++ this.setDead(); ++ this.field_146042_b.fishEntity = null; ++ return 0; ++ } ++ ++ // CraftBukkit end + double d0 = this.field_146042_b.posX - this.posX; + double d2 = this.field_146042_b.posY - this.posY; + double d4 = this.field_146042_b.posZ - this.posZ; +@@ -522,6 +542,19 @@ + else if (this.field_146045_ax > 0) + { + EntityItem entityitem = new EntityItem(this.worldObj, this.posX, this.posY, this.posZ, this.func_146033_f()); ++ // CraftBukkit start ++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.field_146042_b.getBukkitEntity(), entityitem.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH); ++ playerFishEvent.setExpToDrop(this.rand.nextInt(6) + 1); ++ this.worldObj.getServer().getPluginManager().callEvent(playerFishEvent); ++ ++ if (playerFishEvent.isCancelled()) ++ { ++ this.setDead(); ++ this.field_146042_b.fishEntity = null; ++ return 0; ++ } ++ ++ // CraftBukkit end + double d1 = this.field_146042_b.posX - this.posX; + double d3 = this.field_146042_b.posY - this.posY; + double d5 = this.field_146042_b.posZ - this.posZ; +@@ -531,15 +564,36 @@ + entityitem.motionY = d3 * d9 + (double)MathHelper.sqrt_double(d7) * 0.08D; + entityitem.motionZ = d5 * d9; + this.worldObj.spawnEntityInWorld(entityitem); +- this.field_146042_b.worldObj.spawnEntityInWorld(new EntityXPOrb(this.field_146042_b.worldObj, this.field_146042_b.posX, this.field_146042_b.posY + 0.5D, this.field_146042_b.posZ + 0.5D, this.rand.nextInt(6) + 1)); ++ // CraftBukkit - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop() ++ this.field_146042_b.worldObj.spawnEntityInWorld(new EntityXPOrb(this.field_146042_b.worldObj, this.field_146042_b.posX, this.field_146042_b.posY + 0.5D, this.field_146042_b.posZ + 0.5D, playerFishEvent.getExpToDrop())); + b0 = 1; + } + + if (this.field_146051_au) + { ++ // CraftBukkit start ++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.field_146042_b.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND); ++ this.worldObj.getServer().getPluginManager().callEvent(playerFishEvent); ++ ++ if (playerFishEvent.isCancelled()) ++ { ++ this.setDead(); ++ this.field_146042_b.fishEntity = null; ++ return 0; ++ } ++ ++ // CraftBukkit end + b0 = 2; + } + ++ // CraftBukkit start ++ if (b0 == 0) ++ { ++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.field_146042_b.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT); ++ this.worldObj.getServer().getPluginManager().callEvent(playerFishEvent); ++ } ++ ++ // CraftBukkit end + this.setDead(); + this.field_146042_b.fishEntity = null; + return b0; +@@ -580,7 +634,7 @@ + { + float f3 = f - f2; + this.field_146042_b.addStat(StatList.fishCaughtStat, 1); +- return ((WeightedRandomFishable)WeightedRandom.getRandomItem(this.rand, field_146036_f)).func_150708_a(this.rand); ++ return ((WeightedRandomFishable) WeightedRandom.getRandomItem(this.rand, EntityFishHook.field_146036_f)).func_150708_a(this.rand); // CraftBukkit - fix static reference to fish list + } + } + } diff --git a/patches/net/minecraft/entity/projectile/EntityLargeFireball.java.patch b/patches/net/minecraft/entity/projectile/EntityLargeFireball.java.patch new file mode 100644 index 0000000..b948d35 --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntityLargeFireball.java.patch @@ -0,0 +1,40 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntityLargeFireball.java +@@ -9,6 +9,8 @@ + import net.minecraft.util.MovingObjectPosition; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit ++ + public class EntityLargeFireball extends EntityFireball + { + public int field_92057_e = 1; +@@ -39,7 +41,17 @@ + p_70227_1_.entityHit.attackEntityFrom(DamageSource.causeFireballDamage(this, this.shootingEntity), 6.0F); + } + +- this.worldObj.newExplosion((Entity)null, this.posX, this.posY, this.posZ, (float)this.field_92057_e, true, this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")); ++ // CraftBukkit start ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(this.worldObj.getServer(), this)); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ // give 'this' instead of (Entity) null so we know what causes the damage ++ this.worldObj.newExplosion(this, this.posX, this.posY, this.posZ, event.getRadius(), event.getFire(), this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")); ++ } ++ ++ // CraftBukkit end + this.setDead(); + } + } +@@ -56,7 +68,8 @@ + + if (p_70037_1_.hasKey("ExplosionPower", 99)) + { +- this.field_92057_e = p_70037_1_.getInteger("ExplosionPower"); ++ // CraftBukkit - set bukkitYield when setting explosionpower ++ this.bukkitYield = this.field_92057_e = p_70037_1_.getInteger("ExplosionPower"); + } + } + } diff --git a/patches/net/minecraft/entity/projectile/EntityPotion.java.patch b/patches/net/minecraft/entity/projectile/EntityPotion.java.patch new file mode 100644 index 0000000..98d96f7 --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntityPotion.java.patch @@ -0,0 +1,89 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntityPotion.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntityPotion.java +@@ -14,9 +14,16 @@ + import net.minecraft.util.MovingObjectPosition; + import net.minecraft.world.World; + ++// CraftBukkit start ++import java.util.HashMap; ++import net.minecraft.entity.player.EntityPlayerMP; ++import org.bukkit.craftbukkit.entity.CraftLivingEntity; ++import org.bukkit.entity.LivingEntity; ++// CraftBukkit end ++ + public class EntityPotion extends EntityThrowable + { +- private ItemStack potionDamage; ++ public ItemStack potionDamage; // CraftBukkit private --> public + private static final String __OBFID = "CL_00001727"; + + public EntityPotion(World p_i1788_1_) +@@ -88,14 +95,16 @@ + { + List list = Items.potionitem.getEffects(this.potionDamage); + +- if (list != null && !list.isEmpty()) ++ if (true || list != null && !list.isEmpty()) // CraftBukkit - Call event even if no effects to apply + { + AxisAlignedBB axisalignedbb = this.boundingBox.expand(4.0D, 2.0D, 4.0D); + List list1 = this.worldObj.getEntitiesWithinAABB(EntityLivingBase.class, axisalignedbb); + +- if (list1 != null && !list1.isEmpty()) ++ if (list1 != null) // CraftBukkit - Run code even if there are no entities around + { + Iterator iterator = list1.iterator(); ++ // CraftBukkit ++ HashMap affected = new HashMap(); + + while (iterator.hasNext()) + { +@@ -111,6 +120,25 @@ + d1 = 1.0D; + } + ++ // CraftBukkit start ++ affected.put((LivingEntity) entitylivingbase.getBukkitEntity(), d1); ++ } ++ } ++ ++ org.bukkit.event.entity.PotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPotionSplashEvent(this, affected); ++ ++ if (!event.isCancelled() && list != null && !list.isEmpty()) // do not process effects if there are no effects to process ++ { ++ for (LivingEntity victim : event.getAffectedEntities()) ++ { ++ if (!(victim instanceof CraftLivingEntity)) ++ { ++ continue; ++ } ++ ++ EntityLivingBase entitylivingbase = ((CraftLivingEntity) victim).getHandle(); ++ double d1 = event.getIntensity(victim); ++ // CraftBukkit end + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) +@@ -118,9 +146,22 @@ + PotionEffect potioneffect = (PotionEffect)iterator1.next(); + int i = potioneffect.getPotionID(); + ++ // CraftBukkit start - Abide by PVP settings - for players only! ++ if (!this.worldObj.pvpMode && this.getThrower() instanceof EntityPlayerMP && entitylivingbase instanceof EntityPlayerMP && entitylivingbase != this.getThrower()) ++ { ++ // Block SLOWER_MOVEMENT, SLOWER_DIG, HARM, BLINDNESS, HUNGER, WEAKNESS and POISON potions ++ if (i == 2 || i == 4 || i == 7 || i == 15 || i == 17 || i == 18 || i == 19) ++ { ++ continue; ++ } ++ } ++ ++ // CraftBukkit end ++ + if (Potion.potionTypes[i].isInstant()) + { +- Potion.potionTypes[i].affectEntity(this.getThrower(), entitylivingbase, potioneffect.getAmplifier(), d1); ++ // CraftBukkit - Added 'this' ++ Potion.potionTypes[i].applyInstantEffect(this.getThrower(), entitylivingbase, potioneffect.getAmplifier(), d1, this); + } + else + { diff --git a/patches/net/minecraft/entity/projectile/EntitySmallFireball.java.patch b/patches/net/minecraft/entity/projectile/EntitySmallFireball.java.patch new file mode 100644 index 0000000..bebfed8 --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntitySmallFireball.java.patch @@ -0,0 +1,44 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntitySmallFireball.java +@@ -6,6 +6,8 @@ + import net.minecraft.util.MovingObjectPosition; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.EntityCombustByEntityEvent; // CraftBukkit ++ + public class EntitySmallFireball extends EntityFireball + { + private static final String __OBFID = "CL_00001721"; +@@ -36,7 +38,16 @@ + { + if (!p_70227_1_.entityHit.isImmuneToFire() && p_70227_1_.entityHit.attackEntityFrom(DamageSource.causeFireballDamage(this, this.shootingEntity), 5.0F)) + { +- p_70227_1_.entityHit.setFire(5); ++ // CraftBukkit start - Entity damage by entity event + combust event ++ EntityCombustByEntityEvent event = new EntityCombustByEntityEvent((org.bukkit.entity.Projectile) this.getBukkitEntity(), p_70227_1_.entityHit.getBukkitEntity(), 5); ++ p_70227_1_.entityHit.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ p_70227_1_.entityHit.setFire(event.getDuration()); ++ } ++ ++ // CraftBukkit end + } + } + else +@@ -68,7 +79,13 @@ + + if (this.worldObj.isAirBlock(i, j, k)) + { +- this.worldObj.setBlock(i, j, k, Blocks.fire); ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(worldObj, i, j, k, this).isCancelled()) ++ { ++ this.worldObj.setBlock(i, j, k, Blocks.fire); ++ } ++ ++ // CraftBukkit end + } + } + diff --git a/patches/net/minecraft/entity/projectile/EntityThrowable.java.patch b/patches/net/minecraft/entity/projectile/EntityThrowable.java.patch new file mode 100644 index 0000000..6905edb --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntityThrowable.java.patch @@ -0,0 +1,45 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntityThrowable.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntityThrowable.java +@@ -24,8 +24,8 @@ + private Block field_145785_f; + protected boolean inGround; + public int throwableShake; +- private EntityLivingBase thrower; +- private String throwerName; ++ public EntityLivingBase thrower; // CraftBukkit - private -> public ++ public String throwerName; // CraftBukkit - private -> public + private int ticksInGround; + private int ticksInAir; + private static final String __OBFID = "CL_00001723"; +@@ -50,6 +50,7 @@ + { + super(p_i1777_1_); + this.thrower = p_i1777_2_; ++ this.projectileSource = (org.bukkit.entity.LivingEntity) p_i1777_2_.getBukkitEntity(); // CraftBukkit + this.setSize(0.25F, 0.25F); + this.setLocationAndAngles(p_i1777_2_.posX, p_i1777_2_.posY + (double)p_i1777_2_.getEyeHeight(), p_i1777_2_.posZ, p_i1777_2_.rotationYaw, p_i1777_2_.rotationPitch); + this.posX -= (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F); +@@ -187,7 +188,7 @@ + + if (movingobjectposition1 != null) + { +- double d1 = vec3.distanceTo(movingobjectposition1.hitVec); ++ double d1 = vec3.squareDistanceTo(movingobjectposition1.hitVec); // CraftBukkit - distance efficiency + + if (d1 < d0 || d0 == 0.0D) + { +@@ -213,6 +214,14 @@ + else + { + this.onImpact(movingobjectposition); ++ ++ // CraftBukkit start ++ if (this.isDead) ++ { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); ++ } ++ ++ // CraftBukkit end + } + } + diff --git a/patches/net/minecraft/entity/projectile/EntityWitherSkull.java.patch b/patches/net/minecraft/entity/projectile/EntityWitherSkull.java.patch new file mode 100644 index 0000000..cb1d4fd --- /dev/null +++ b/patches/net/minecraft/entity/projectile/EntityWitherSkull.java.patch @@ -0,0 +1,38 @@ +--- ../src-base/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.java ++++ ../src-work/minecraft/net/minecraft/entity/projectile/EntityWitherSkull.java +@@ -13,6 +13,8 @@ + import net.minecraft.world.Explosion; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit ++ + public class EntityWitherSkull extends EntityFireball + { + private static final String __OBFID = "CL_00001728"; +@@ -68,7 +70,7 @@ + { + if (p_70227_1_.entityHit.attackEntityFrom(DamageSource.causeMobDamage(this.shootingEntity), 8.0F) && !p_70227_1_.entityHit.isEntityAlive()) + { +- this.shootingEntity.heal(5.0F); ++ this.shootingEntity.heal(5.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER); // CraftBukkit + } + } + else +@@ -96,7 +98,16 @@ + } + } + +- this.worldObj.newExplosion(this, this.posX, this.posY, this.posZ, 1.0F, false, this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")); ++ // CraftBukkit start ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.worldObj.newExplosion(this, this.posX, this.posY, this.posZ, event.getRadius(), event.getFire(), this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")); ++ } ++ ++ // CraftBukkit end + this.setDead(); + } + } diff --git a/patches/net/minecraft/init/Bootstrap.java.patch b/patches/net/minecraft/init/Bootstrap.java.patch new file mode 100644 index 0000000..42d25de --- /dev/null +++ b/patches/net/minecraft/init/Bootstrap.java.patch @@ -0,0 +1,454 @@ +--- ../src-base/minecraft/net/minecraft/init/Bootstrap.java ++++ ../src-work/minecraft/net/minecraft/init/Bootstrap.java +@@ -1,6 +1,7 @@ + package net.minecraft.init; + + import java.util.Random; ++ + import net.minecraft.block.Block; + import net.minecraft.block.BlockDispenser; + import net.minecraft.block.BlockFire; +@@ -34,6 +35,11 @@ + import net.minecraft.util.EnumFacing; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + public class Bootstrap + { + private static boolean field_151355_a = false; +@@ -116,14 +122,48 @@ + double d0 = p_82487_1_.getX() + (double)enumfacing.getFrontOffsetX(); + double d1 = (double)((float)p_82487_1_.getYInt() + 0.2F); + double d2 = p_82487_1_.getZ() + (double)enumfacing.getFrontOffsetZ(); +- Entity entity = ItemMonsterPlacer.spawnCreature(p_82487_1_.getWorld(), p_82487_2_.getItemDamage(), d0, d1, d2); ++ // CraftBukkit start ++ World world = p_82487_1_.getWorld(); ++ ItemStack itemstack1 = p_82487_2_.splitStack(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2)); + ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ p_82487_2_.stackSize++; ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ p_82487_2_.stackSize++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ ++ itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); ++ Entity entity = ItemMonsterPlacer.spawnCreature(p_82487_1_.getWorld(), p_82487_2_.getItemDamage(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); ++ + if (entity instanceof EntityLivingBase && p_82487_2_.hasDisplayName()) + { +- ((EntityLiving)entity).setCustomNameTag(p_82487_2_.getDisplayName()); ++ ((EntityLiving) entity).setCustomNameTag(p_82487_2_.getDisplayName()); + } + +- p_82487_2_.splitStack(1); ++ // p_82487_2_.splitStack(1); // Handled during event processing ++ // CraftBukkit end + return p_82487_2_; + } + }); +@@ -136,9 +176,43 @@ + double d0 = p_82487_1_.getX() + (double)enumfacing.getFrontOffsetX(); + double d1 = (double)((float)p_82487_1_.getYInt() + 0.2F); + double d2 = p_82487_1_.getZ() + (double)enumfacing.getFrontOffsetZ(); +- EntityFireworkRocket entityfireworkrocket = new EntityFireworkRocket(p_82487_1_.getWorld(), d0, d1, d2, p_82487_2_); ++ // CraftBukkit start ++ World world = p_82487_1_.getWorld(); ++ ItemStack itemstack1 = p_82487_2_.splitStack(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2)); ++ ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ p_82487_2_.stackSize++; ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ p_82487_2_.stackSize++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ ++ itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); ++ EntityFireworkRocket entityfireworkrocket = new EntityFireworkRocket(p_82487_1_.getWorld(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), itemstack1); + p_82487_1_.getWorld().spawnEntityInWorld(entityfireworkrocket); +- p_82487_2_.splitStack(1); ++ // p_82487_2_.splitStack(1); // Handled during event processing ++ // CraftBukkit end + return p_82487_2_; + } + protected void playDispenseSound(IBlockSource p_82485_1_) +@@ -161,8 +235,42 @@ + double d3 = random.nextGaussian() * 0.05D + (double)enumfacing.getFrontOffsetX(); + double d4 = random.nextGaussian() * 0.05D + (double)enumfacing.getFrontOffsetY(); + double d5 = random.nextGaussian() * 0.05D + (double)enumfacing.getFrontOffsetZ(); +- world.spawnEntityInWorld(new EntitySmallFireball(world, d0, d1, d2, d3, d4, d5)); +- p_82487_2_.splitStack(1); ++ // CraftBukkit start ++ ItemStack itemstack1 = p_82487_2_.splitStack(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d3, d4, d5)); ++ ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ p_82487_2_.stackSize++; ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ p_82487_2_.stackSize++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ ++ EntitySmallFireball entitysmallfireball = new EntitySmallFireball(world, d0, d1, d2, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); ++ entitysmallfireball.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) p_82487_1_.getBlockTileEntity()); ++ world.spawnEntityInWorld(entitysmallfireball); ++ // p_82487_2_.splitStack(1); // Handled during event processing ++ // CraftBukkit end + return p_82487_2_; + } + protected void playDispenseSound(IBlockSource p_82485_1_) +@@ -201,9 +309,41 @@ + d3 = 0.0D; + } + +- EntityBoat entityboat = new EntityBoat(world, d0, d1 + d3, d2); ++ // CraftBukkit start ++ ItemStack itemstack1 = p_82487_2_.splitStack(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2)); ++ ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ p_82487_2_.stackSize++; ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ p_82487_2_.stackSize++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ ++ EntityBoat entityboat = new EntityBoat(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); ++ // CraftBukkit end + world.spawnEntityInWorld(entityboat); +- p_82487_2_.splitStack(1); ++ // p_82487_2_.splitStack(1); // CraftBukkit - handled during event processing + return p_82487_2_; + } + protected void playDispenseSound(IBlockSource p_82485_1_) +@@ -217,16 +357,67 @@ + private static final String __OBFID = "CL_00001399"; + public ItemStack dispenseStack(IBlockSource p_82487_1_, ItemStack p_82487_2_) + { +- ItemBucket itembucket = (ItemBucket)p_82487_2_.getItem(); ++ ItemBucket itembucket = (ItemBucket) p_82487_2_.getItem(); + int i = p_82487_1_.getXInt(); + int j = p_82487_1_.getYInt(); + int k = p_82487_1_.getZInt(); + EnumFacing enumfacing = BlockDispenser.func_149937_b(p_82487_1_.getBlockMetadata()); ++ // CraftBukkit start ++ World world = p_82487_1_.getWorld(); ++ int x = i + enumfacing.getFrontOffsetX(); ++ int y = j + enumfacing.getFrontOffsetY(); ++ int z = k + enumfacing.getFrontOffsetZ(); + ++ if (world.isAirBlock(x, y, z) || !world.getBlock(x, y, z).getMaterial().isSolid()) ++ { ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(i, j, k); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(p_82487_2_); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z)); ++ ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ ++ itembucket = (ItemBucket) CraftItemStack.asNMSCopy(event.getItem()).getItem(); ++ } ++ ++ // CraftBukkit end ++ + if (itembucket.tryPlaceContainedLiquid(p_82487_1_.getWorld(), i + enumfacing.getFrontOffsetX(), j + enumfacing.getFrontOffsetY(), k + enumfacing.getFrontOffsetZ())) + { +- p_82487_2_.func_150996_a(Items.bucket); +- p_82487_2_.stackSize = 1; ++ // CraftBukkit start - Handle stacked buckets ++ Item item = Items.bucket; ++ ++ if (--p_82487_2_.stackSize == 0) ++ { ++ p_82487_2_.func_150996_a(Items.bucket); ++ p_82487_2_.stackSize = 1; ++ } ++ else if (((TileEntityDispenser) p_82487_1_.getBlockTileEntity()).func_146019_a(new ItemStack(item)) < 0) ++ { ++ this.field_150841_b.dispense(p_82487_1_, new ItemStack(item)); ++ } ++ ++ // CraftBukkit end + return p_82487_2_; + } + else +@@ -266,6 +457,36 @@ + item = Items.lava_bucket; + } + ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(p_82487_2_); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(i, j, k)); ++ ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ ++ // CraftBukkit end ++ + world.setBlockToAir(i, j, k); + + if (--p_82487_2_.stackSize == 0) +@@ -292,16 +513,51 @@ + int i = p_82487_1_.getXInt() + enumfacing.getFrontOffsetX(); + int j = p_82487_1_.getYInt() + enumfacing.getFrontOffsetY(); + int k = p_82487_1_.getZInt() + enumfacing.getFrontOffsetZ(); ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(p_82487_2_); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + +- if (world.isAirBlock(i, j, k)) ++ if (!BlockDispenser.eventFired) + { +- world.setBlock(i, j, k, Blocks.fire); ++ world.getServer().getPluginManager().callEvent(event); ++ } + +- if (p_82487_2_.attemptDamageItem(1, world.rand)) ++ if (event.isCancelled()) ++ { ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) + { +- p_82487_2_.stackSize = 0; ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; + } + } ++ ++ // CraftBukkit end ++ ++ if (world.isAirBlock(i, j, k)) ++ { ++ // CraftBukkit start - Ignition by dispensing flint and steel ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, i, j, k, p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()).isCancelled()) ++ { ++ world.setBlock(i, j, k, Blocks.fire); ++ ++ if (p_82487_2_.attemptDamageItem(1, world.rand)) ++ { ++ p_82487_2_.stackSize = 0; ++ } ++ } ++ ++ // CraftBukkit end ++ } + else if (world.getBlock(i, j, k) == Blocks.tnt) + { + Blocks.tnt.onBlockDestroyedByPlayer(world, i, j, k, 1); +@@ -339,7 +595,36 @@ + int i = p_82487_1_.getXInt() + enumfacing.getFrontOffsetX(); + int j = p_82487_1_.getYInt() + enumfacing.getFrontOffsetY(); + int k = p_82487_1_.getZInt() + enumfacing.getFrontOffsetZ(); ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asNewCraftStack(p_82487_2_.getItem()); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ ++ // CraftBukkit end ++ + if (ItemDye.func_150919_a(p_82487_2_, world, i, j, k)) + { + if (!world.isRemote) +@@ -381,9 +666,41 @@ + int i = p_82487_1_.getXInt() + enumfacing.getFrontOffsetX(); + int j = p_82487_1_.getYInt() + enumfacing.getFrontOffsetY(); + int k = p_82487_1_.getZInt() + enumfacing.getFrontOffsetZ(); +- EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double)((float)i + 0.5F), (double)((float)j + 0.5F), (double)((float)k + 0.5F), (EntityLivingBase)null); ++ // CraftBukkit start ++ ItemStack itemstack1 = p_82487_2_.splitStack(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(p_82487_1_.getXInt(), p_82487_1_.getYInt(), p_82487_1_.getZInt()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(i + 0.5, j + 0.5, k + 0.5)); ++ ++ if (!BlockDispenser.eventFired) ++ { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ p_82487_2_.stackSize++; ++ return p_82487_2_; ++ } ++ ++ if (!event.getItem().equals(craftItem)) ++ { ++ p_82487_2_.stackSize++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IBehaviorDispenseItem ibehaviordispenseitem = (IBehaviorDispenseItem) BlockDispenser.dispenseBehaviorRegistry.getObject(eventStack.getItem()); ++ ++ if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider && ibehaviordispenseitem != this) ++ { ++ ibehaviordispenseitem.dispense(p_82487_1_, eventStack); ++ return p_82487_2_; ++ } ++ } ++ ++ EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (EntityLivingBase) null); ++ // CraftBukkit end + world.spawnEntityInWorld(entitytntprimed); +- --p_82487_2_.stackSize; ++ // --p_82487_2_.stackSize; // CraftBukkit - handled above + return p_82487_2_; + } + }); diff --git a/patches/net/minecraft/inventory/AnimalChest.java.patch b/patches/net/minecraft/inventory/AnimalChest.java.patch new file mode 100644 index 0000000..7972403 --- /dev/null +++ b/patches/net/minecraft/inventory/AnimalChest.java.patch @@ -0,0 +1,83 @@ +--- ../src-base/minecraft/net/minecraft/inventory/AnimalChest.java ++++ ../src-work/minecraft/net/minecraft/inventory/AnimalChest.java +@@ -3,6 +3,14 @@ + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import net.minecraft.entity.passive.EntityHorse; ++import net.minecraft.item.ItemStack; ++// CraftBukkit end ++ + public class AnimalChest extends InventoryBasic + { + private static final String __OBFID = "CL_00001731"; +@@ -12,6 +20,65 @@ + super(p_i1796_1_, false, p_i1796_2_); + } + ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private EntityHorse horse; ++ private int maxStack = MAX_STACK; ++ ++ public AnimalChest(String s, int i, EntityHorse horse) ++ { ++ this(s, i); ++ this.horse = horse; ++ } ++ ++ @Override ++ public ItemStack[] getContents() ++ { ++ return this.inventoryContents; ++ } ++ ++ @Override ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ @Override ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ @Override ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ @Override ++ public org.bukkit.inventory.InventoryHolder getOwner() ++ { ++ return (org.bukkit.entity.Horse) this.horse.getBukkitEntity(); ++ } ++ ++ @Override ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ ++ @Override ++ ++ /** ++ * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't ++ * this more of a set than a get?* ++ */ ++ public int getInventoryStackLimit() ++ { ++ return maxStack; ++ } ++ // CraftBukkit end ++ + @SideOnly(Side.CLIENT) + public AnimalChest(String p_i1797_1_, boolean p_i1797_2_, int p_i1797_3_) + { diff --git a/patches/net/minecraft/inventory/Container.java.patch b/patches/net/minecraft/inventory/Container.java.patch new file mode 100644 index 0000000..2cfc727 --- /dev/null +++ b/patches/net/minecraft/inventory/Container.java.patch @@ -0,0 +1,186 @@ +--- ../src-base/minecraft/net/minecraft/inventory/Container.java ++++ ../src-work/minecraft/net/minecraft/inventory/Container.java +@@ -13,6 +13,17 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.util.MathHelper; + ++// CraftBukkit start ++import java.util.HashMap; ++import java.util.Map; ++import net.minecraft.entity.player.EntityPlayerMP; ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.Event.Result; ++import org.bukkit.event.inventory.InventoryDragEvent; ++import org.bukkit.inventory.InventoryView; ++// CraftBukkit end ++ + public abstract class Container + { + public List inventoryItemStacks = new ArrayList(); +@@ -21,12 +32,52 @@ + @SideOnly(Side.CLIENT) + private short transactionID; + private int field_94535_f = -1; +- private int field_94536_g; ++ public int field_94536_g; // CraftBukkit - private -> public + private final Set field_94537_h = new HashSet(); ++ public InventoryView bukkitView = null; // Cauldron ++ + protected List crafters = new ArrayList(); + private Set playerList = new HashSet(); + private static final String __OBFID = "CL_00001730"; + ++ // CraftBukkit start ++ public boolean checkReachable = true; ++ public InventoryView getBukkitView() { return bukkitView; } // Cauldron ++ public void transferTo(Container other, org.bukkit.craftbukkit.entity.CraftHumanEntity player) ++ { ++ InventoryView source = this.getBukkitView(), destination = other.getBukkitView(); ++ // Cauldron start - add null checks to skip modded inventories with no Bukkit wrappers, and ++ // catch AbstractMethodErrors for modded IInventory's with no onClose() ++ if (source != null) { ++ try { ++ ((CraftInventory) source.getTopInventory()).getInventory().onClose(player); ++ } catch (AbstractMethodError ex) { ++ // modded ++ } ++ ++ try { ++ ((CraftInventory) source.getBottomInventory()).getInventory().onClose(player); ++ } catch (AbstractMethodError ex) { ++ // modded ++ } ++ } ++ if (destination != null) { ++ try { ++ ((CraftInventory) destination.getTopInventory()).getInventory().onOpen(player); ++ } catch (AbstractMethodError ex) { ++ // modded ++ } ++ ++ try { ++ ((CraftInventory) destination.getBottomInventory()).getInventory().onOpen(player); ++ } catch (AbstractMethodError ex) { ++ // modded ++ } ++ } ++ // Cauldron end ++ } ++ // CraftBukkit end ++ + protected Slot addSlotToContainer(Slot p_75146_1_) + { + p_75146_1_.slotNumber = this.inventorySlots.size(); +@@ -39,7 +90,11 @@ + { + if (this.crafters.contains(p_75132_1_)) + { +- throw new IllegalArgumentException("Listener already listening"); ++ // Cauldron start - As we do not create a new player object on respawn, we need to update the client with changes if listener already exists ++ //throw new IllegalArgumentException("Listener already listening"); ++ p_75132_1_.sendContainerAndContentsToPlayer(this, this.getInventory()); ++ this.detectAndSendChanges(); ++ // Cauldron end + } + else + { +@@ -109,6 +164,10 @@ + + public Slot getSlot(int p_75139_1_) + { ++ // Cauldron start - vanilla compatibility. fixes NPE with ProjectRed's Item Stock Keeper ++ if (p_75139_1_ < 0 || p_75139_1_ >= this.inventorySlots.size()) ++ return null; ++ // Cauldron end + return (Slot)this.inventorySlots.get(p_75139_1_); + } + +@@ -168,6 +227,7 @@ + itemstack3 = inventoryplayer.getItemStack().copy(); + i1 = inventoryplayer.getItemStack().stackSize; + Iterator iterator = this.field_94537_h.iterator(); ++ Map draggedSlots = new HashMap(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) + + while (iterator.hasNext()) + { +@@ -190,18 +250,55 @@ + } + + i1 -= itemstack1.stackSize - j1; +- slot1.putStack(itemstack1); ++ draggedSlots.put(slot1.slotNumber, itemstack1); // CraftBukkit - Put in map instead of setting + } + } + +- itemstack3.stackSize = i1; ++ // CraftBukkit start - InventoryDragEvent ++ InventoryView view = getBukkitView(); ++ org.bukkit.inventory.ItemStack newcursor = CraftItemStack.asCraftMirror(itemstack3); ++ newcursor.setAmount(i1); ++ Map eventmap = new HashMap(); + +- if (itemstack3.stackSize <= 0) ++ for (Map.Entry ditem : draggedSlots.entrySet()) + { +- itemstack3 = null; ++ eventmap.put(ditem.getKey(), CraftItemStack.asBukkitCopy(ditem.getValue())); + } + +- inventoryplayer.setItemStack(itemstack3); ++ // It's essential that we set the cursor to the new value here to prevent item duplication if a plugin closes the inventory. ++ ItemStack oldCursor = inventoryplayer.getItemStack(); ++ inventoryplayer.setItemStack(CraftItemStack.asNMSCopy(newcursor)); ++ InventoryDragEvent event = new InventoryDragEvent(view, (newcursor.getType() != org.bukkit.Material.AIR ? newcursor : null), CraftItemStack.asBukkitCopy(oldCursor), this.field_94535_f == i1, eventmap); // Should be dragButton ++ p_75144_4_.worldObj.getServer().getPluginManager().callEvent(event); ++ // Whether or not a change was made to the inventory that requires an update. ++ boolean needsUpdate = event.getResult() != Result.DEFAULT; ++ ++ if (event.getResult() != Result.DENY) ++ { ++ for (Map.Entry dslot : draggedSlots.entrySet()) ++ { ++ view.setItem(dslot.getKey(), CraftItemStack.asBukkitCopy(dslot.getValue())); ++ } ++ ++ // The only time the carried item will be set to null is if the inventory is closed by the server. ++ // If the inventory is closed by the server, then the cursor items are dropped. This is why we change the cursor early. ++ if (inventoryplayer.getItemStack() != null) ++ { ++ inventoryplayer.setItemStack(CraftItemStack.asNMSCopy(event.getCursor())); ++ needsUpdate = true; ++ } ++ } ++ else ++ { ++ inventoryplayer.setItemStack(oldCursor); ++ } ++ ++ if (needsUpdate && p_75144_4_ instanceof EntityPlayerMP) ++ { ++ ((EntityPlayerMP) p_75144_4_).sendContainerToPlayer(this); ++ } ++ ++ // CraftBukkit end + } + + this.func_94533_d(); +@@ -235,10 +332,17 @@ + + if (p_75144_2_ == 1) + { +- p_75144_4_.dropPlayerItemWithRandomChoice(inventoryplayer.getItemStack().splitStack(1), true); ++ // CraftBukkit start - Store a reference ++ ItemStack itemstack4 = inventoryplayer.getItemStack(); + + if (inventoryplayer.getItemStack().stackSize == 0) + { ++ p_75144_4_.dropPlayerItemWithRandomChoice(inventoryplayer.getItemStack().splitStack(1), true); ++ } ++ ++ if (itemstack4.stackSize == 0) ++ { ++ // CraftBukkit end + inventoryplayer.setItemStack((ItemStack)null); + } + } diff --git a/patches/net/minecraft/inventory/ContainerBeacon.java.patch b/patches/net/minecraft/inventory/ContainerBeacon.java.patch new file mode 100644 index 0000000..edb50c4 --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerBeacon.java.patch @@ -0,0 +1,62 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerBeacon.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerBeacon.java +@@ -8,6 +8,8 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.tileentity.TileEntityBeacon; + ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit ++ + public class ContainerBeacon extends Container + { + private TileEntityBeacon tileBeacon; +@@ -15,10 +17,15 @@ + private int field_82865_g; + private int field_82867_h; + private int field_82868_i; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; ++ // CraftBukkit end + private static final String __OBFID = "CL_00001735"; + + public ContainerBeacon(InventoryPlayer p_i1802_1_, TileEntityBeacon p_i1802_2_) + { ++ player = p_i1802_1_; // CraftBukkit + this.tileBeacon = p_i1802_2_; + this.addSlotToContainer(this.beaconSlot = new ContainerBeacon.BeaconSlot(p_i1802_2_, 0, 136, 110)); + byte b0 = 36; +@@ -77,6 +84,13 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ // CraftBukkit start ++ if (!this.checkReachable) ++ { ++ return true; ++ } ++ // CraftBukkit end ++ + return this.tileBeacon.isUseableByPlayer(p_75145_1_); + } + +@@ -145,6 +159,20 @@ + return itemstack; + } + ++ // CraftBukkit start ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ org.bukkit.craftbukkit.inventory.CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryBeacon(this.tileBeacon); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end ++ + class BeaconSlot extends Slot + { + private static final String __OBFID = "CL_00001736"; diff --git a/patches/net/minecraft/inventory/ContainerBrewingStand.java.patch b/patches/net/minecraft/inventory/ContainerBrewingStand.java.patch new file mode 100644 index 0000000..0aa2974 --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerBrewingStand.java.patch @@ -0,0 +1,63 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerBrewingStand.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerBrewingStand.java +@@ -10,15 +10,25 @@ + import net.minecraft.stats.AchievementList; + import net.minecraft.tileentity.TileEntityBrewingStand; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventoryBrewer; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerBrewingStand extends Container + { + private TileEntityBrewingStand tileBrewingStand; + private final Slot theSlot; + private int brewTime; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; ++ // CraftBukkit end + private static final String __OBFID = "CL_00001737"; + + public ContainerBrewingStand(InventoryPlayer p_i1805_1_, TileEntityBrewingStand p_i1805_2_) + { ++ this.player = p_i1805_1_; // CraftBukkit + this.tileBrewingStand = p_i1805_2_; + this.addSlotToContainer(new ContainerBrewingStand.Potion(p_i1805_1_.player, p_i1805_2_, 0, 56, 46)); + this.addSlotToContainer(new ContainerBrewingStand.Potion(p_i1805_1_.player, p_i1805_2_, 1, 79, 53)); +@@ -74,6 +84,13 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ // CraftBukkit start ++ if (!this.checkReachable) ++ { ++ return true; ++ } ++ // CraftBukkit end ++ + return this.tileBrewingStand.isUseableByPlayer(p_75145_1_); + } + +@@ -152,6 +169,20 @@ + return itemstack; + } + ++ // CraftBukkit start ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryBrewer inventory = new CraftInventoryBrewer(this.tileBrewingStand); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end ++ + class Ingredient extends Slot + { + private static final String __OBFID = "CL_00001738"; diff --git a/patches/net/minecraft/inventory/ContainerChest.java.patch b/patches/net/minecraft/inventory/ContainerChest.java.patch new file mode 100644 index 0000000..1db5e5c --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerChest.java.patch @@ -0,0 +1,77 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerChest.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerChest.java +@@ -3,10 +3,46 @@ + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.item.ItemStack; + ++// CraftBukkit start ++import net.minecraft.entity.player.InventoryPlayer; ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerChest extends Container + { +- private IInventory lowerChestInventory; ++ public IInventory lowerChestInventory; // CraftBukkit - private->public + private int numRows; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; ++ ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null || player == null) // Cauldron ++ { ++ return bukkitEntity; ++ } ++ ++ CraftInventory inventory; ++ ++ if (this.lowerChestInventory instanceof InventoryPlayer) ++ { ++ inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryPlayer((InventoryPlayer) this.lowerChestInventory); ++ } ++ else if (this.lowerChestInventory instanceof InventoryLargeChest) ++ { ++ inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) this.lowerChestInventory); ++ } ++ else ++ { ++ inventory = new CraftInventory(this.lowerChestInventory); ++ } ++ ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00001742"; + + public ContainerChest(IInventory p_i1806_1_, IInventory p_i1806_2_) +@@ -15,6 +51,12 @@ + this.numRows = p_i1806_2_.getSizeInventory() / 9; + p_i1806_2_.openInventory(); + int i = (this.numRows - 4) * 18; ++ // CraftBukkit start - Save player ++ if (p_i1806_1_ instanceof InventoryPlayer) // Cauldron - make sure it is an InventoryPlayer before casting ++ { ++ this.player = (InventoryPlayer) p_i1806_1_; ++ } ++ // CraftBukkit end + int j; + int k; + +@@ -42,6 +84,13 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ // CraftBukkit start ++ if (!this.checkReachable) ++ { ++ return true; ++ } ++ // CraftBukkit end ++ + return this.lowerChestInventory.isUseableByPlayer(p_75145_1_); + } + diff --git a/patches/net/minecraft/inventory/ContainerDispenser.java.patch b/patches/net/minecraft/inventory/ContainerDispenser.java.patch new file mode 100644 index 0000000..e1578ab --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerDispenser.java.patch @@ -0,0 +1,65 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerDispenser.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerDispenser.java +@@ -4,14 +4,28 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.tileentity.TileEntityDispenser; + ++// CraftBukkit start ++import net.minecraft.entity.player.InventoryPlayer; ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerDispenser extends Container + { +- private TileEntityDispenser tileDispenser; ++ public TileEntityDispenser tileDispenser; // CraftBukkit - private -> public ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; ++ // CraftBukkit end + private static final String __OBFID = "CL_00001763"; + + public ContainerDispenser(IInventory p_i1825_1_, TileEntityDispenser p_i1825_2_) + { + this.tileDispenser = p_i1825_2_; ++ // CraftBukkit start - Save player ++ // TODO: Should we check to make sure it really is an InventoryPlayer? ++ this.player = (InventoryPlayer)p_i1825_1_; ++ // CraftBukkit end + int i; + int j; + +@@ -39,6 +53,13 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ // CraftBukkit start ++ if (!this.checkReachable) ++ { ++ return true; ++ } ++ // CraftBukkit end ++ + return this.tileDispenser.isUseableByPlayer(p_75145_1_); + } + +@@ -83,4 +104,18 @@ + + return itemstack; + } ++ ++ // CraftBukkit start ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ CraftInventory inventory = new CraftInventory(this.tileDispenser); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/inventory/ContainerEnchantment.java.patch b/patches/net/minecraft/inventory/ContainerEnchantment.java.patch new file mode 100644 index 0000000..a838cdf --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerEnchantment.java.patch @@ -0,0 +1,208 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerEnchantment.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerEnchantment.java +@@ -4,6 +4,8 @@ + import cpw.mods.fml.relauncher.SideOnly; + import java.util.List; + import java.util.Random; ++ ++import net.minecraft.enchantment.Enchantment; + import net.minecraft.enchantment.EnchantmentData; + import net.minecraft.enchantment.EnchantmentHelper; + import net.minecraft.entity.player.EntityPlayer; +@@ -14,21 +16,22 @@ + import net.minecraft.world.World; + import net.minecraftforge.common.ForgeHooks; + ++// CraftBukkit start ++import java.util.Map; ++ ++import org.bukkit.craftbukkit.inventory.CraftInventoryEnchanting; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.enchantment.EnchantItemEvent; ++import org.bukkit.event.enchantment.PrepareItemEnchantEvent; ++import org.bukkit.entity.Player; ++// CraftBukkit end ++ + public class ContainerEnchantment extends Container + { +- public IInventory tableInventory = new InventoryBasic("Enchant", true, 1) +- { +- private static final String __OBFID = "CL_00001746"; +- public int getInventoryStackLimit() +- { +- return 1; +- } +- public void markDirty() +- { +- super.markDirty(); +- ContainerEnchantment.this.onCraftMatrixChanged(this); +- } +- }; ++ // CraftBukkit - make type specific (changed from IInventory) ++ public ContainerEnchantTableInventory tableInventory_CB = new ContainerEnchantTableInventory(this, "Enchant", true, 1); // CraftBukkit ++ public IInventory tableInventory = tableInventory_CB; + private World worldPointer; + private int posX; + private int posY; +@@ -36,6 +39,10 @@ + private Random rand = new Random(); + public long nameSeed; + public int[] enchantLevels = new int[3]; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private Player player; ++ // CraftBukkit end + private static final String __OBFID = "CL_00001745"; + + public ContainerEnchantment(InventoryPlayer p_i1811_1_, World p_i1811_2_, int p_i1811_3_, int p_i1811_4_, int p_i1811_5_) +@@ -66,6 +73,11 @@ + { + this.addSlotToContainer(new Slot(p_i1811_1_, l, 8 + l * 18, 142)); + } ++ ++ // CraftBukkit start ++ player = (Player) p_i1811_1_.player.getBukkitEntity(); ++ tableInventory_CB.player = player; // Cauldron ++ // CraftBukkit end + } + + public void addCraftingToCrafters(ICrafting p_75132_1_) +@@ -109,7 +121,7 @@ + ItemStack itemstack = p_75130_1_.getStackInSlot(0); + int i; + +- if (itemstack != null && itemstack.isItemEnchantable()) ++ if (itemstack != null) // CraftBukkit - relax condition + { + this.nameSeed = this.rand.nextLong(); + +@@ -144,6 +156,23 @@ + this.enchantLevels[j] = EnchantmentHelper.calcItemStackEnchantability(this.rand, j, (int)power, itemstack); + } + ++ // CraftBukkit start ++ CraftItemStack item = CraftItemStack.asCraftMirror(itemstack); ++ PrepareItemEnchantEvent event = new PrepareItemEnchantEvent(player, this.getBukkitView(), this.worldPointer.getWorld().getBlockAt(this.posX, this.posY, this.posZ), item, this.enchantLevels, i); ++ event.setCancelled(!itemstack.isItemEnchantable()); ++ if (this.getBukkitView() != null) this.worldPointer.getServer().getPluginManager().callEvent(event); // Cauldron - allow vanilla mods to byp ++ ++ if (event.isCancelled()) ++ { ++ for (i = 0; i < 3; ++i) ++ { ++ this.enchantLevels[i] = 0; ++ } ++ ++ return; ++ } ++ ++ // CraftBukkit end + this.detectAndSendChanges(); + } + } +@@ -170,32 +199,64 @@ + + if (list != null) + { +- p_75140_1_.addExperienceLevel(-this.enchantLevels[p_75140_2_]); ++ // CraftBukkit start ++ Map enchants = new java.util.HashMap(); + +- if (flag) ++ for (Object obj : list) + { +- itemstack.func_150996_a(Items.enchanted_book); ++ EnchantmentData instance = (EnchantmentData) obj; ++ enchants.put(org.bukkit.enchantments.Enchantment.getById(instance.enchantmentobj.effectId), instance.enchantmentLevel); + } + +- int j = flag && list.size() > 1 ? this.rand.nextInt(list.size()) : -1; ++ CraftItemStack item = CraftItemStack.asCraftMirror(itemstack); ++ EnchantItemEvent event = new EnchantItemEvent((Player) p_75140_1_.getBukkitEntity(), this.getBukkitView(), this.worldPointer.getWorld().getBlockAt(this.posX, this.posY, this.posZ), item, this.enchantLevels[p_75140_2_], enchants, p_75140_2_); ++ if (this.getBukkitView() != null) this.worldPointer.getServer().getPluginManager().callEvent(event); // Cauldron - allow vanilla mods to bypass ++ int level = event.getExpLevelCost(); + +- for (int k = 0; k < list.size(); ++k) ++ if (event.isCancelled() || (level > p_75140_1_.experienceLevel && !p_75140_1_.capabilities.isCreativeMode) || enchants.isEmpty()) + { +- EnchantmentData enchantmentdata = (EnchantmentData)list.get(k); ++ return false; ++ } + +- if (!flag || k != j) ++ boolean applied = !flag; ++ ++ for (Map.Entry entry : event.getEnchantsToAdd().entrySet()) ++ { ++ try + { + if (flag) + { +- Items.enchanted_book.addEnchantment(itemstack, enchantmentdata); ++ int enchantId = entry.getKey().getId(); ++ ++ if (Enchantment.enchantmentsList[enchantId] == null) ++ { ++ continue; ++ } ++ ++ EnchantmentData enchantment = new EnchantmentData(enchantId, entry.getValue()); ++ Items.enchanted_book.addEnchantment(itemstack, enchantment); ++ applied = true; ++ itemstack.func_150996_a(Items.enchanted_book); ++ break; + } + else + { +- itemstack.addEnchantment(enchantmentdata.enchantmentobj, enchantmentdata.enchantmentLevel); ++ item.addEnchantment(entry.getKey(), entry.getValue()); + } + } ++ catch (IllegalArgumentException e) ++ { ++ /* Just swallow invalid enchantments */ ++ } + } + ++ // Only down level if we've applied the enchantments ++ if (applied) ++ { ++ p_75140_1_.addExperienceLevel(-level); ++ } ++ ++ // CraftBukkit end + this.onCraftMatrixChanged(this.tableInventory); + } + } +@@ -225,6 +286,11 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ if (!this.checkReachable) ++ { ++ return true; // CraftBukkit ++ } ++ + return this.worldPointer.getBlock(this.posX, this.posY, this.posZ) != Blocks.enchanting_table ? false : p_75145_1_.getDistanceSq((double)this.posX + 0.5D, (double)this.posY + 0.5D, (double)this.posZ + 0.5D) <= 64.0D; + } + +@@ -283,4 +349,18 @@ + + return itemstack; + } ++ ++ // CraftBukkit start ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.tableInventory_CB); ++ bukkitEntity = new CraftInventoryView(this.player, inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/inventory/ContainerFurnace.java.patch b/patches/net/minecraft/inventory/ContainerFurnace.java.patch new file mode 100644 index 0000000..c11416a --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerFurnace.java.patch @@ -0,0 +1,57 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerFurnace.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerFurnace.java +@@ -8,12 +8,34 @@ + import net.minecraft.item.crafting.FurnaceRecipes; + import net.minecraft.tileentity.TileEntityFurnace; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventoryFurnace; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerFurnace extends Container + { + private TileEntityFurnace tileFurnace; + private int lastCookTime; + private int lastBurnTime; + private int lastItemBurnTime; ++ ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; ++ ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryFurnace inventory = new CraftInventoryFurnace(this.tileFurnace); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00001748"; + + public ContainerFurnace(InventoryPlayer p_i1812_1_, TileEntityFurnace p_i1812_2_) +@@ -22,6 +44,7 @@ + this.addSlotToContainer(new Slot(p_i1812_2_, 0, 56, 17)); + this.addSlotToContainer(new Slot(p_i1812_2_, 1, 56, 53)); + this.addSlotToContainer(new SlotFurnace(p_i1812_1_.player, p_i1812_2_, 2, 116, 35)); ++ this.player = p_i1812_1_; // CraftBukkit - save player + int i; + + for (i = 0; i < 3; ++i) +@@ -96,6 +119,11 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ if (!this.checkReachable) ++ { ++ return true; // CraftBukkit ++ } ++ + return this.tileFurnace.isUseableByPlayer(p_75145_1_); + } + diff --git a/patches/net/minecraft/inventory/ContainerHopper.java.patch b/patches/net/minecraft/inventory/ContainerHopper.java.patch new file mode 100644 index 0000000..46638ae --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerHopper.java.patch @@ -0,0 +1,51 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerHopper.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerHopper.java +@@ -4,13 +4,36 @@ + import net.minecraft.entity.player.InventoryPlayer; + import net.minecraft.item.ItemStack; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerHopper extends Container + { + private final IInventory field_94538_a; + private static final String __OBFID = "CL_00001750"; + ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; ++ ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ CraftInventory inventory = new CraftInventory(this.field_94538_a); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end ++ + public ContainerHopper(InventoryPlayer p_i1814_1_, IInventory p_i1814_2_) + { ++ this.player = p_i1814_1_; // CraftBukkit - save player + this.field_94538_a = p_i1814_2_; + p_i1814_2_.openInventory(); + byte b0 = 51; +@@ -37,6 +60,11 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ if (!this.checkReachable) ++ { ++ return true; // CraftBukkit ++ } ++ + return this.field_94538_a.isUseableByPlayer(p_75145_1_); + } + diff --git a/patches/net/minecraft/inventory/ContainerMerchant.java.patch b/patches/net/minecraft/inventory/ContainerMerchant.java.patch new file mode 100644 index 0000000..a2b2828 --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerMerchant.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerMerchant.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerMerchant.java +@@ -8,6 +8,8 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit ++ + public class ContainerMerchant extends Container + { + private IMerchant theMerchant; diff --git a/patches/net/minecraft/inventory/ContainerPlayer.java.patch b/patches/net/minecraft/inventory/ContainerPlayer.java.patch new file mode 100644 index 0000000..eb3b6dc --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerPlayer.java.patch @@ -0,0 +1,82 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerPlayer.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerPlayer.java +@@ -12,18 +12,33 @@ + import net.minecraft.item.crafting.CraftingManager; + import net.minecraft.util.IIcon; + ++// CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.network.play.server.S2FPacketSetSlot; ++import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerPlayer extends Container + { + public InventoryCrafting craftMatrix = new InventoryCrafting(this, 2, 2); + public IInventory craftResult = new InventoryCraftResult(); + public boolean isLocalWorld; + private final EntityPlayer thePlayer; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; ++ // CraftBukkit end + private static final String __OBFID = "CL_00001754"; + + public ContainerPlayer(final InventoryPlayer p_i1819_1_, boolean p_i1819_2_, EntityPlayer p_i1819_3_) + { + this.isLocalWorld = p_i1819_2_; + this.thePlayer = p_i1819_3_; ++ this.craftResult = new InventoryCraftResult(); // CraftBukkit - moved to before InventoryCrafting construction ++ this.craftMatrix = new InventoryCrafting(this, 2, 2, p_i1819_1_.player); // CraftBukkit - pass player ++ this.craftMatrix.resultInventory = this.craftResult; // CraftBukkit - let InventoryCrafting know about its result slot ++ this.player = p_i1819_1_; // CraftBukkit - save player + this.addSlotToContainer(new SlotCrafting(p_i1819_1_.player, this.craftMatrix, this.craftResult, 0, 144, 36)); + int i; + int j; +@@ -72,12 +87,24 @@ + this.addSlotToContainer(new Slot(p_i1819_1_, i, 8 + i * 18, 142)); + } + +- this.onCraftMatrixChanged(this.craftMatrix); ++ // this.onCraftMatrixChanged(this.craftMatrix); // CraftBukkit - unneeded since it just sets result slot to empty + } + + public void onCraftMatrixChanged(IInventory p_75130_1_) + { +- this.craftResult.setInventorySlotContents(0, CraftingManager.getInstance().findMatchingRecipe(this.craftMatrix, this.thePlayer.worldObj)); ++ // CraftBukkit start (Note: the following line would cause an error if called during construction) ++ CraftingManager.getInstance().lastCraftView = getBukkitView(); ++ ItemStack craftResult = CraftingManager.getInstance().findMatchingRecipe(this.craftMatrix, this.thePlayer.worldObj); ++ this.craftResult.setInventorySlotContents(0, craftResult); ++ ++ if (super.crafters.size() < 1) ++ { ++ return; ++ } ++ ++ EntityPlayerMP player = (EntityPlayerMP) super.crafters.get(0); // TODO: Is this _always_ correct? Seems like it. ++ player.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(player.openContainer.windowId, 0, craftResult)); ++ // CraftBukkit end + } + + public void onContainerClosed(EntityPlayer p_75134_1_) +@@ -187,4 +214,18 @@ + { + return p_94530_2_.inventory != this.craftResult && super.func_94530_a(p_94530_1_, p_94530_2_); + } ++ ++ // CraftBukkit start ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryCrafting inventory = new CraftInventoryCrafting(this.craftMatrix, this.craftResult); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/inventory/ContainerRepair.java.patch b/patches/net/minecraft/inventory/ContainerRepair.java.patch new file mode 100644 index 0000000..00b9737 --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerRepair.java.patch @@ -0,0 +1,71 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerRepair.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerRepair.java +@@ -17,19 +17,13 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit ++ + public class ContainerRepair extends Container + { + private static final Logger logger = LogManager.getLogger(); + private IInventory outputSlot = new InventoryCraftResult(); +- private IInventory inputSlots = new InventoryBasic("Repair", true, 2) +- { +- private static final String __OBFID = "CL_00001733"; +- public void markDirty() +- { +- super.markDirty(); +- ContainerRepair.this.onCraftMatrixChanged(this); +- } +- }; ++ private IInventory inputSlots = new ContainerRepairInventory(this, "Repair", true, 2); + private World theWorld; + private int field_82861_i; + private int field_82858_j; +@@ -39,9 +33,14 @@ + private String repairedItemName; + private final EntityPlayer thePlayer; + private static final String __OBFID = "CL_00001732"; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; + + public ContainerRepair(InventoryPlayer p_i1800_1_, final World p_i1800_2_, final int p_i1800_3_, final int p_i1800_4_, final int p_i1800_5_, EntityPlayer p_i1800_6_) + { ++ this.player = p_i1800_1_; ++ // CraftBukkit end + this.theWorld = p_i1800_2_; + this.field_82861_i = p_i1800_3_; + this.field_82858_j = p_i1800_4_; +@@ -461,6 +460,11 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ if (!this.checkReachable) ++ { ++ return true; // CraftBukkit ++ } ++ + return this.theWorld.getBlock(this.field_82861_i, this.field_82858_j, this.field_82859_k) != Blocks.anvil ? false : p_75145_1_.getDistanceSq((double)this.field_82861_i + 0.5D, (double)this.field_82858_j + 0.5D, (double)this.field_82859_k + 0.5D) <= 64.0D; + } + +@@ -535,4 +539,18 @@ + + this.updateRepairOutput(); + } ++ ++ // CraftBukkit start ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ org.bukkit.craftbukkit.inventory.CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryAnvil(this.inputSlots, this.outputSlot); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/inventory/ContainerWorkbench.java.patch b/patches/net/minecraft/inventory/ContainerWorkbench.java.patch new file mode 100644 index 0000000..d54c87f --- /dev/null +++ b/patches/net/minecraft/inventory/ContainerWorkbench.java.patch @@ -0,0 +1,92 @@ +--- ../src-base/minecraft/net/minecraft/inventory/ContainerWorkbench.java ++++ ../src-work/minecraft/net/minecraft/inventory/ContainerWorkbench.java +@@ -7,18 +7,35 @@ + import net.minecraft.item.crafting.CraftingManager; + import net.minecraft.world.World; + ++// CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.network.play.server.S2FPacketSetSlot; ++import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerWorkbench extends Container + { +- public InventoryCrafting craftMatrix = new InventoryCrafting(this, 3, 3); +- public IInventory craftResult = new InventoryCraftResult(); ++ public InventoryCrafting craftMatrix; // CraftBukkit - move initialization into constructor ++ public IInventory craftResult; // CraftBukkit - move initialization into constructor + private World worldObj; + private int posX; + private int posY; + private int posZ; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private InventoryPlayer player; ++ // CraftBukkit end + private static final String __OBFID = "CL_00001744"; + + public ContainerWorkbench(InventoryPlayer p_i1808_1_, World p_i1808_2_, int p_i1808_3_, int p_i1808_4_, int p_i1808_5_) + { ++ // CraftBukkit start - Switched order of IInventory construction and stored player ++ this.craftResult = new InventoryCraftResult(); ++ this.craftMatrix = new InventoryCrafting(this, 3, 3, p_i1808_1_.player); // CraftBukkit - pass player ++ this.craftMatrix.resultInventory = this.craftResult; ++ this.player = p_i1808_1_; ++ // CraftBukkit end + this.worldObj = p_i1808_2_; + this.posX = p_i1808_3_; + this.posY = p_i1808_4_; +@@ -53,7 +70,19 @@ + + public void onCraftMatrixChanged(IInventory p_75130_1_) + { +- this.craftResult.setInventorySlotContents(0, CraftingManager.getInstance().findMatchingRecipe(this.craftMatrix, this.worldObj)); ++ // CraftBukkit start ++ CraftingManager.getInstance().lastCraftView = getBukkitView(); ++ ItemStack craftResult = CraftingManager.getInstance().findMatchingRecipe(this.craftMatrix, this.worldObj); ++ this.craftResult.setInventorySlotContents(0, craftResult); ++ ++ if (super.crafters.size() < 1) ++ { ++ return; ++ } ++ ++ EntityPlayerMP player = (EntityPlayerMP) super.crafters.get(0); // TODO: Is this _always_ correct? Seems like it. ++ player.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(player.openContainer.windowId, 0, craftResult)); ++ // CraftBukkit end + } + + public void onContainerClosed(EntityPlayer p_75134_1_) +@@ -76,6 +105,11 @@ + + public boolean canInteractWith(EntityPlayer p_75145_1_) + { ++ if (!this.checkReachable) ++ { ++ return true; // CraftBukkit ++ } ++ + return this.worldObj.getBlock(this.posX, this.posY, this.posZ) != Blocks.crafting_table ? false : p_75145_1_.getDistanceSq((double)this.posX + 0.5D, (double)this.posY + 0.5D, (double)this.posZ + 0.5D) <= 64.0D; + } + +@@ -141,4 +175,18 @@ + { + return p_94530_2_.inventory != this.craftResult && super.func_94530_a(p_94530_1_, p_94530_2_); + } ++ ++ // CraftBukkit start ++ public CraftInventoryView getBukkitView() ++ { ++ if (bukkitEntity != null) ++ { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryCrafting inventory = new CraftInventoryCrafting(this.craftMatrix, this.craftResult); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/inventory/IInventory.java.patch b/patches/net/minecraft/inventory/IInventory.java.patch new file mode 100644 index 0000000..cd33ce3 --- /dev/null +++ b/patches/net/minecraft/inventory/IInventory.java.patch @@ -0,0 +1,32 @@ +--- ../src-base/minecraft/net/minecraft/inventory/IInventory.java ++++ ../src-work/minecraft/net/minecraft/inventory/IInventory.java +@@ -3,6 +3,8 @@ + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.item.ItemStack; + ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; // CraftBukkit ++ + public interface IInventory + { + int getSizeInventory(); +@@ -30,4 +32,20 @@ + void closeInventory(); + + boolean isItemValidForSlot(int p_94041_1_, ItemStack p_94041_2_); ++ ++ // CraftBukkit start ++ ItemStack[] getContents(); ++ ++ void onOpen(CraftHumanEntity who); ++ ++ void onClose(CraftHumanEntity who); ++ ++ java.util.List getViewers(); ++ ++ org.bukkit.inventory.InventoryHolder getOwner(); ++ ++ void setMaxStackSize(int size); ++ ++ int MAX_STACK = 64; ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/inventory/InventoryBasic.java.patch b/patches/net/minecraft/inventory/InventoryBasic.java.patch new file mode 100644 index 0000000..9a76868 --- /dev/null +++ b/patches/net/minecraft/inventory/InventoryBasic.java.patch @@ -0,0 +1,57 @@ +--- ../src-base/minecraft/net/minecraft/inventory/InventoryBasic.java ++++ ../src-work/minecraft/net/minecraft/inventory/InventoryBasic.java +@@ -2,6 +2,11 @@ + + import java.util.ArrayList; + import java.util.List; ++ ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.inventory.InventoryHolder; ++ + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.item.ItemStack; + +@@ -9,7 +14,7 @@ + { + private String inventoryTitle; + private int slotsCount; +- private ItemStack[] inventoryContents; ++ protected ItemStack[] inventoryContents; // CraftBukkit - protected + private List field_70480_d; + private boolean field_94051_e; + private static final String __OBFID = "CL_00001514"; +@@ -150,4 +155,33 @@ + { + return true; + } ++ ++ // Cauldron start ++ @Override ++ public ItemStack[] getContents() ++ { ++ return null; ++ } ++ ++ @Override ++ public void onOpen(CraftHumanEntity who) {} ++ ++ @Override ++ public void onClose(CraftHumanEntity who) {} ++ ++ @Override ++ public List getViewers() ++ { ++ return null; ++ } ++ ++ @Override ++ public InventoryHolder getOwner() ++ { ++ return null; ++ } ++ ++ @Override ++ public void setMaxStackSize(int size) {} ++ // Cauldron end + } diff --git a/patches/net/minecraft/inventory/InventoryCraftResult.java.patch b/patches/net/minecraft/inventory/InventoryCraftResult.java.patch new file mode 100644 index 0000000..1d17965 --- /dev/null +++ b/patches/net/minecraft/inventory/InventoryCraftResult.java.patch @@ -0,0 +1,53 @@ +--- ../src-base/minecraft/net/minecraft/inventory/InventoryCraftResult.java ++++ ../src-work/minecraft/net/minecraft/inventory/InventoryCraftResult.java +@@ -2,10 +2,41 @@ + + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.item.ItemStack; ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end + + public class InventoryCraftResult implements IInventory + { + private ItemStack[] stackResult = new ItemStack[1]; ++ ++ // CraftBukkit start ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return this.stackResult; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() ++ { ++ return null; // Result slots don't get an owner ++ } ++ ++ // Don't need a transaction; the InventoryCrafting keeps track of it for us ++ public void onOpen(CraftHumanEntity who) {} ++ public void onClose(CraftHumanEntity who) {} ++ public java.util.List getViewers() ++ { ++ return new java.util.ArrayList(); ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00001760"; + + public int getSizeInventory() +@@ -63,7 +94,7 @@ + + public int getInventoryStackLimit() + { +- return 64; ++ return maxStack; // CraftBukkit + } + + public void markDirty() {} diff --git a/patches/net/minecraft/inventory/InventoryCrafting.java.patch b/patches/net/minecraft/inventory/InventoryCrafting.java.patch new file mode 100644 index 0000000..fd0f85d --- /dev/null +++ b/patches/net/minecraft/inventory/InventoryCrafting.java.patch @@ -0,0 +1,81 @@ +--- ../src-base/minecraft/net/minecraft/inventory/InventoryCrafting.java ++++ ../src-work/minecraft/net/minecraft/inventory/InventoryCrafting.java +@@ -3,11 +3,69 @@ + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.item.ItemStack; + ++// CraftBukkit start ++import java.util.List; ++import net.minecraft.item.crafting.IRecipe; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.event.inventory.InventoryType; ++// CraftBukkit end ++ + public class InventoryCrafting implements IInventory + { + private ItemStack[] stackList; + private int inventoryWidth; + private Container eventHandler; ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ public IRecipe currentRecipe; ++ public IInventory resultInventory; ++ private EntityPlayer owner; ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return this.stackList; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public InventoryType getInvType() ++ { ++ return stackList.length == 4 ? InventoryType.CRAFTING : InventoryType.WORKBENCH; ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() ++ { ++ if (owner == null) return null; // Cauldron ++ return owner.getBukkitEntity(); ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ resultInventory.setMaxStackSize(size); ++ } ++ ++ public InventoryCrafting(Container container, int i, int j, EntityPlayer player) ++ { ++ this(container, i, j); ++ this.owner = player; ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00001743"; + + public InventoryCrafting(Container p_i1807_1_, int p_i1807_2_, int p_i1807_3_) +@@ -105,7 +163,7 @@ + + public int getInventoryStackLimit() + { +- return 64; ++ return maxStack; // CraftBukkit + } + + public void markDirty() {} diff --git a/patches/net/minecraft/inventory/InventoryEnderChest.java.patch b/patches/net/minecraft/inventory/InventoryEnderChest.java.patch new file mode 100644 index 0000000..c145959 --- /dev/null +++ b/patches/net/minecraft/inventory/InventoryEnderChest.java.patch @@ -0,0 +1,64 @@ +--- ../src-base/minecraft/net/minecraft/inventory/InventoryEnderChest.java ++++ ../src-work/minecraft/net/minecraft/inventory/InventoryEnderChest.java +@@ -6,9 +6,61 @@ + import net.minecraft.nbt.NBTTagList; + import net.minecraft.tileentity.TileEntityEnderChest; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ ++ + public class InventoryEnderChest extends InventoryBasic + { + private TileEntityEnderChest associatedChest; ++ ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ public org.bukkit.entity.Player player; ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return this.inventoryContents; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() ++ { ++ return this.player; ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ ++ /** ++ * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't ++ * this more of a set than a get?* ++ */ ++ public int getInventoryStackLimit() ++ { ++ return maxStack; ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00001759"; + + public InventoryEnderChest() diff --git a/patches/net/minecraft/inventory/InventoryLargeChest.java.patch b/patches/net/minecraft/inventory/InventoryLargeChest.java.patch new file mode 100644 index 0000000..875ad96 --- /dev/null +++ b/patches/net/minecraft/inventory/InventoryLargeChest.java.patch @@ -0,0 +1,76 @@ +--- ../src-base/minecraft/net/minecraft/inventory/InventoryLargeChest.java ++++ ../src-work/minecraft/net/minecraft/inventory/InventoryLargeChest.java +@@ -3,11 +3,62 @@ + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.item.ItemStack; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventoryLargeChest implements IInventory + { + private String name; +- private IInventory upperChest; +- private IInventory lowerChest; ++ public IInventory upperChest; // CraftBukkit - private -> public ++ public IInventory lowerChest; // CraftBukkit - private -> public ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ ++ public ItemStack[] getContents() ++ { ++ ItemStack[] result = new ItemStack[this.getSizeInventory()]; ++ ++ for (int i = 0; i < result.length; i++) ++ { ++ result[i] = this.getStackInSlot(i); ++ } ++ ++ return result; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ this.upperChest.onOpen(who); ++ this.lowerChest.onOpen(who); ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ this.upperChest.onClose(who); ++ this.lowerChest.onClose(who); ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() ++ { ++ return null; // This method won't be called since CraftInventoryDoubleChest doesn't defer to here ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ this.upperChest.setMaxStackSize(size); ++ this.lowerChest.setMaxStackSize(size); ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00001507"; + + public InventoryLargeChest(String p_i1559_1_, IInventory p_i1559_2_, IInventory p_i1559_3_) +@@ -77,7 +128,7 @@ + + public int getInventoryStackLimit() + { +- return this.upperChest.getInventoryStackLimit(); ++ return Math.min(this.upperChest.getInventoryStackLimit(), this.lowerChest.getInventoryStackLimit()); // CraftBukkit - check both sides + } + + public void markDirty() diff --git a/patches/net/minecraft/inventory/InventoryMerchant.java.patch b/patches/net/minecraft/inventory/InventoryMerchant.java.patch new file mode 100644 index 0000000..e22abf0 --- /dev/null +++ b/patches/net/minecraft/inventory/InventoryMerchant.java.patch @@ -0,0 +1,65 @@ +--- ../src-base/minecraft/net/minecraft/inventory/InventoryMerchant.java ++++ ../src-work/minecraft/net/minecraft/inventory/InventoryMerchant.java +@@ -6,6 +6,12 @@ + import net.minecraft.village.MerchantRecipe; + import net.minecraft.village.MerchantRecipeList; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventoryMerchant implements IInventory + { + private final IMerchant theMerchant; +@@ -13,6 +19,40 @@ + private final EntityPlayer thePlayer; + private MerchantRecipe currentRecipe; + private int currentRecipeIndex; ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return this.theInventory; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int i) ++ { ++ maxStack = i; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() ++ { ++ return thePlayer.getBukkitEntity(); ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00001756"; + + public InventoryMerchant(EntityPlayer p_i1820_1_, IMerchant p_i1820_2_) +@@ -124,7 +164,7 @@ + + public int getInventoryStackLimit() + { +- return 64; ++ return maxStack; // CraftBukkit + } + + public boolean isUseableByPlayer(EntityPlayer p_70300_1_) diff --git a/patches/net/minecraft/inventory/Slot.java.patch b/patches/net/minecraft/inventory/Slot.java.patch new file mode 100644 index 0000000..221d1b8 --- /dev/null +++ b/patches/net/minecraft/inventory/Slot.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/inventory/Slot.java ++++ ../src-work/minecraft/net/minecraft/inventory/Slot.java +@@ -10,7 +10,7 @@ + + public class Slot + { +- private final int slotIndex; ++ public final int slotIndex; // CraftBukkit - private -> public + public final IInventory inventory; + public int slotNumber; + public int xDisplayPosition; diff --git a/patches/net/minecraft/inventory/SlotFurnace.java.patch b/patches/net/minecraft/inventory/SlotFurnace.java.patch new file mode 100644 index 0000000..9c36934 --- /dev/null +++ b/patches/net/minecraft/inventory/SlotFurnace.java.patch @@ -0,0 +1,36 @@ +--- ../src-base/minecraft/net/minecraft/inventory/SlotFurnace.java ++++ ../src-work/minecraft/net/minecraft/inventory/SlotFurnace.java +@@ -9,6 +9,12 @@ + import net.minecraft.stats.AchievementList; + import net.minecraft.util.MathHelper; + ++// CraftBukkit start ++import net.minecraft.tileentity.TileEntityFurnace; ++import org.bukkit.entity.Player; ++import org.bukkit.event.inventory.FurnaceExtractEvent; ++// CraftBukkit end ++ + public class SlotFurnace extends Slot + { + private EntityPlayer thePlayer; +@@ -74,6 +80,20 @@ + i = j; + } + ++ // Cauldron start - validate inventory before attempting to cast it ++ if (this.inventory instanceof TileEntityFurnace) ++ { ++ // CraftBukkit start ++ Player player = (Player) thePlayer.getBukkitEntity(); ++ TileEntityFurnace furnace = ((TileEntityFurnace) this.inventory); ++ org.bukkit.block.Block block = thePlayer.worldObj.getWorld().getBlockAt(furnace.xCoord, furnace.yCoord, furnace.zCoord); ++ FurnaceExtractEvent event = new FurnaceExtractEvent(player, block, org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(p_75208_1_.getItem()), p_75208_1_.stackSize, i); ++ thePlayer.worldObj.getServer().getPluginManager().callEvent(event); ++ i = event.getExpToDrop(); ++ // CraftBukkit end ++ } ++ // Cauldron end ++ + while (i > 0) + { + j = EntityXPOrb.getXPSplit(i); diff --git a/patches/net/minecraft/item/Item.java.patch b/patches/net/minecraft/item/Item.java.patch new file mode 100644 index 0000000..d504e19 --- /dev/null +++ b/patches/net/minecraft/item/Item.java.patch @@ -0,0 +1,38 @@ +--- ../src-base/minecraft/net/minecraft/item/Item.java ++++ ../src-work/minecraft/net/minecraft/item/Item.java +@@ -54,6 +54,7 @@ + import net.minecraft.util.WeightedRandomChestContent; + import net.minecraft.world.World; + import net.minecraftforge.common.ChestGenHooks; ++import net.minecraftforge.common.DimensionManager; // Cauldron + import net.minecraftforge.common.util.EnumHelper; + + public class Item +@@ -402,6 +403,12 @@ + { + object = (new ItemDoublePlant(Blocks.double_plant, Blocks.double_plant, BlockDoublePlant.field_149892_a)).setUnlocalizedName("doublePlant"); + } ++ // CraftBukkit start - allow certain blocks to retain data ++ else if (block == Blocks.mob_spawner || block == Blocks.brown_mushroom_block || block == Blocks.red_mushroom_block) ++ { ++ object = new ItemColored(block, true); ++ } ++ // CraftBukkit end + else + { + if (hashset.contains(block)) +@@ -923,7 +930,13 @@ + */ + public int getEntityLifespan(ItemStack itemStack, World world) + { +- return 6000; ++ // Cauldron start - fixes MFR proxy worlds used with grinder/slaughterhouse ++ if (world == null) ++ { ++ return 6000; ++ } ++ return world.getSpigotConfig().itemDespawnRate; // Spigot ++ // Cauldron end + } + + /** diff --git a/patches/net/minecraft/item/ItemBoat.java.patch b/patches/net/minecraft/item/ItemBoat.java.patch new file mode 100644 index 0000000..0e0f669 --- /dev/null +++ b/patches/net/minecraft/item/ItemBoat.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemBoat.java ++++ ../src-work/minecraft/net/minecraft/item/ItemBoat.java +@@ -80,7 +80,16 @@ + i = movingobjectposition.blockX; + int j = movingobjectposition.blockY; + int k = movingobjectposition.blockZ; ++ // CraftBukkit start - Boat placement ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(p_77659_3_, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, i, j, k, movingobjectposition.sideHit, p_77659_1_); + ++ if (event.isCancelled()) ++ { ++ return p_77659_1_; ++ } ++ ++ // CraftBukkit end ++ + if (p_77659_2_.getBlock(i, j, k) == Blocks.snow_layer) + { + --j; diff --git a/patches/net/minecraft/item/ItemBow.java.patch b/patches/net/minecraft/item/ItemBow.java.patch new file mode 100644 index 0000000..17c9d5e --- /dev/null +++ b/patches/net/minecraft/item/ItemBow.java.patch @@ -0,0 +1,33 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemBow.java ++++ ../src-work/minecraft/net/minecraft/item/ItemBow.java +@@ -84,6 +84,21 @@ + entityarrow.setFire(100); + } + ++ // CraftBukkit start ++ org.bukkit.event.entity.EntityShootBowEvent cbEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(p_77615_3_, p_77615_1_, entityarrow, f); ++ ++ if (cbEvent.isCancelled()) ++ { ++ cbEvent.getProjectile().remove(); ++ return; ++ } ++ ++ if (cbEvent.getProjectile() == entityarrow.getBukkitEntity()) ++ { ++ p_77615_2_.spawnEntityInWorld(entityarrow); ++ } ++ ++ // CraftBukkit end + p_77615_1_.damageItem(1, p_77615_3_); + p_77615_2_.playSoundAtEntity(p_77615_3_, "random.bow", 1.0F, 1.0F / (itemRand.nextFloat() * 0.4F + 1.2F) + f * 0.5F); + +@@ -98,7 +113,7 @@ + + if (!p_77615_2_.isRemote) + { +- p_77615_2_.spawnEntityInWorld(entityarrow); ++ // p_77615_2_.spawnEntityInWorld(entityarrow); // CraftBukkit - moved up + } + } + } diff --git a/patches/net/minecraft/item/ItemBucket.java.patch b/patches/net/minecraft/item/ItemBucket.java.patch new file mode 100644 index 0000000..6c70ce0 --- /dev/null +++ b/patches/net/minecraft/item/ItemBucket.java.patch @@ -0,0 +1,133 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemBucket.java ++++ ../src-work/minecraft/net/minecraft/item/ItemBucket.java +@@ -12,6 +12,13 @@ + import net.minecraftforge.common.MinecraftForge; + import net.minecraftforge.event.entity.player.FillBucketEvent; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.player.PlayerBucketEmptyEvent; ++import org.bukkit.event.player.PlayerBucketFillEvent; ++// CraftBukkit end ++ + public class ItemBucket extends Item + { + private Block isFull; +@@ -83,23 +90,52 @@ + + if (material == Material.water && l == 0) + { ++ // CraftBukkit start ++ PlayerBucketFillEvent cbEvent = CraftEventFactory.callPlayerBucketFillEvent(p_77659_3_, i, j, k, -1, p_77659_1_, Items.water_bucket); ++ ++ if (cbEvent.isCancelled()) ++ { ++ return p_77659_1_; ++ } ++ ++ // CraftBukkit end + p_77659_2_.setBlockToAir(i, j, k); +- return this.func_150910_a(p_77659_1_, p_77659_3_, Items.water_bucket); ++ return this.func_150910_a(p_77659_1_, p_77659_3_, Items.water_bucket, cbEvent.getItemStack()); // CraftBukkit - added Event stack + } + + if (material == Material.lava && l == 0) + { ++ // CraftBukkit start ++ PlayerBucketFillEvent cbEvent = CraftEventFactory.callPlayerBucketFillEvent(p_77659_3_, i, j, k, -1, p_77659_1_, Items.lava_bucket); ++ ++ if (cbEvent.isCancelled()) ++ { ++ return p_77659_1_; ++ } ++ ++ // CraftBukkit end + p_77659_2_.setBlockToAir(i, j, k); +- return this.func_150910_a(p_77659_1_, p_77659_3_, Items.lava_bucket); ++ return this.func_150910_a(p_77659_1_, p_77659_3_, Items.lava_bucket, cbEvent.getItemStack()); // CraftBukkit - added Event stack + } + } + else + { + if (this.isFull == Blocks.air) + { +- return new ItemStack(Items.bucket); ++ // CraftBukkit start ++ PlayerBucketEmptyEvent cbEvent = CraftEventFactory.callPlayerBucketEmptyEvent(p_77659_3_, i, j, k, movingobjectposition.sideHit, p_77659_1_); ++ ++ if (cbEvent.isCancelled()) ++ { ++ return p_77659_1_; ++ } ++ ++ return CraftItemStack.asNMSCopy(cbEvent.getItemStack()); + } + ++ int clickedX = i, clickedY = j, clickedZ = k; ++ // CraftBukkit end ++ + if (movingobjectposition.sideHit == 0) + { + --j; +@@ -135,9 +171,19 @@ + return p_77659_1_; + } + ++ // CraftBukkit start ++ PlayerBucketEmptyEvent cbEvent = CraftEventFactory.callPlayerBucketEmptyEvent(p_77659_3_, clickedX, clickedY, clickedZ, movingobjectposition.sideHit, p_77659_1_); ++ ++ if (cbEvent.isCancelled()) ++ { ++ return p_77659_1_; ++ } ++ ++ // CraftBukkit end ++ + if (this.tryPlaceContainedLiquid(p_77659_2_, i, j, k) && !p_77659_3_.capabilities.isCreativeMode) + { +- return new ItemStack(Items.bucket); ++ return CraftItemStack.asNMSCopy(cbEvent.getItemStack()); // CraftBukkit + } + } + } +@@ -146,24 +192,32 @@ + } + } + ++ // Cauldron start - vanilla compatibility + private ItemStack func_150910_a(ItemStack p_150910_1_, EntityPlayer p_150910_2_, Item p_150910_3_) + { +- if (p_150910_2_.capabilities.isCreativeMode) ++ return this.func_150910_a(p_150910_1_, p_150910_2_, p_150910_3_, null); ++ } ++ // Cauldron end ++ ++ // CraftBukkit - added ob.ItemStack result - TODO: Is this... the right way to handle this? ++ private ItemStack func_150910_a(ItemStack itemstack, EntityPlayer entityplayer, Item item, org.bukkit.inventory.ItemStack result) ++ { ++ if (entityplayer.capabilities.isCreativeMode) + { +- return p_150910_1_; ++ return itemstack; + } +- else if (--p_150910_1_.stackSize <= 0) ++ else if (--itemstack.stackSize <= 0) + { +- return new ItemStack(p_150910_3_); ++ return CraftItemStack.asNMSCopy(result); // CraftBukkit + } + else + { +- if (!p_150910_2_.inventory.addItemStackToInventory(new ItemStack(p_150910_3_))) ++ if (!entityplayer.inventory.addItemStackToInventory(CraftItemStack.asNMSCopy(result))) // CraftBukkit + { +- p_150910_2_.dropPlayerItemWithRandomChoice(new ItemStack(p_150910_3_, 1, 0), false); ++ entityplayer.dropPlayerItemWithRandomChoice(CraftItemStack.asNMSCopy(result), false); // CraftBukkit + } + +- return p_150910_1_; ++ return itemstack; + } + } + diff --git a/patches/net/minecraft/item/ItemDye.java.patch b/patches/net/minecraft/item/ItemDye.java.patch new file mode 100644 index 0000000..ef52624 --- /dev/null +++ b/patches/net/minecraft/item/ItemDye.java.patch @@ -0,0 +1,30 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemDye.java ++++ ../src-work/minecraft/net/minecraft/item/ItemDye.java +@@ -23,6 +23,8 @@ + import net.minecraftforge.common.util.FakePlayerFactory; + import net.minecraftforge.event.entity.player.BonemealEvent; + ++import org.bukkit.event.entity.SheepDyeWoolEvent; // CraftBukkit ++ + public class ItemDye extends Item + { + public static final String[] field_150923_a = new String[] {"black", "red", "green", "brown", "blue", "purple", "cyan", "silver", "gray", "pink", "lime", "yellow", "lightBlue", "magenta", "orange", "white"}; +@@ -220,6 +222,18 @@ + + if (!entitysheep.getSheared() && entitysheep.getFleeceColor() != i) + { ++ // CraftBukkit start ++ byte bColor = (byte) i; ++ SheepDyeWoolEvent event = new SheepDyeWoolEvent((org.bukkit.entity.Sheep) entitysheep.getBukkitEntity(), org.bukkit.DyeColor.getByData(bColor)); ++ entitysheep.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return false; ++ } ++ ++ i = (byte) event.getColor().getWoolData(); ++ // CraftBukkit end + entitysheep.setFleeceColor(i); + --p_111207_1_.stackSize; + } diff --git a/patches/net/minecraft/item/ItemEmptyMap.java.patch b/patches/net/minecraft/item/ItemEmptyMap.java.patch new file mode 100644 index 0000000..b72e669 --- /dev/null +++ b/patches/net/minecraft/item/ItemEmptyMap.java.patch @@ -0,0 +1,10 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemEmptyMap.java ++++ ../src-work/minecraft/net/minecraft/item/ItemEmptyMap.java +@@ -27,6 +27,7 @@ + mapdata.zCenter = (int)(Math.round(p_77659_3_.posZ / (double)i) * (long)i); + mapdata.dimension = p_77659_2_.provider.dimensionId; + mapdata.markDirty(); ++ org.bukkit.craftbukkit.event.CraftEventFactory.callEvent(new org.bukkit.event.server.MapInitializeEvent(mapdata.mapView)); // CraftBukkit + --p_77659_1_.stackSize; + + if (p_77659_1_.stackSize <= 0) diff --git a/patches/net/minecraft/item/ItemFireball.java.patch b/patches/net/minecraft/item/ItemFireball.java.patch new file mode 100644 index 0000000..f2043a7 --- /dev/null +++ b/patches/net/minecraft/item/ItemFireball.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemFireball.java ++++ ../src-work/minecraft/net/minecraft/item/ItemFireball.java +@@ -61,6 +61,18 @@ + { + if (p_77648_3_.getBlock(p_77648_4_, p_77648_5_, p_77648_6_).getMaterial() == Material.air) + { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(p_77648_3_, p_77648_4_, p_77648_5_, p_77648_6_, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FIREBALL, p_77648_2_).isCancelled()) ++ { ++ if (!p_77648_2_.capabilities.isCreativeMode) ++ { ++ --p_77648_1_.stackSize; ++ } ++ ++ return false; ++ } ++ ++ // CraftBukkit end + p_77648_3_.playSoundEffect((double)p_77648_4_ + 0.5D, (double)p_77648_5_ + 0.5D, (double)p_77648_6_ + 0.5D, "fire.ignite", 1.0F, itemRand.nextFloat() * 0.4F + 0.8F); + p_77648_3_.setBlock(p_77648_4_, p_77648_5_, p_77648_6_, Blocks.fire); + } diff --git a/patches/net/minecraft/item/ItemFishingRod.java.patch b/patches/net/minecraft/item/ItemFishingRod.java.patch new file mode 100644 index 0000000..dc5ce58 --- /dev/null +++ b/patches/net/minecraft/item/ItemFishingRod.java.patch @@ -0,0 +1,35 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemFishingRod.java ++++ ../src-work/minecraft/net/minecraft/item/ItemFishingRod.java +@@ -9,6 +9,8 @@ + import net.minecraft.util.IIcon; + import net.minecraft.world.World; + ++import org.bukkit.event.player.PlayerFishEvent; // CraftBukkit ++ + public class ItemFishingRod extends Item + { + @SideOnly(Side.CLIENT) +@@ -44,11 +46,22 @@ + } + else + { ++ // CraftBukkit start ++ EntityFishHook hook = new EntityFishHook(p_77659_2_, p_77659_3_); ++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((org.bukkit.entity.Player) p_77659_3_.getBukkitEntity(), null, (org.bukkit.entity.Fish) hook.getBukkitEntity(), PlayerFishEvent.State.FISHING); ++ p_77659_2_.getServer().getPluginManager().callEvent(playerFishEvent); ++ ++ if (playerFishEvent.isCancelled()) ++ { ++ return p_77659_1_; ++ } ++ ++ // CraftBukkit end + p_77659_2_.playSoundAtEntity(p_77659_3_, "random.bow", 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + + if (!p_77659_2_.isRemote) + { +- p_77659_2_.spawnEntityInWorld(new EntityFishHook(p_77659_2_, p_77659_3_)); ++ p_77659_2_.spawnEntityInWorld(hook); // CraftBukkit - moved creation up + } + + p_77659_3_.swingItem(); diff --git a/patches/net/minecraft/item/ItemFlintAndSteel.java.patch b/patches/net/minecraft/item/ItemFlintAndSteel.java.patch new file mode 100644 index 0000000..971c0e4 --- /dev/null +++ b/patches/net/minecraft/item/ItemFlintAndSteel.java.patch @@ -0,0 +1,51 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemFlintAndSteel.java ++++ ../src-work/minecraft/net/minecraft/item/ItemFlintAndSteel.java +@@ -6,6 +6,11 @@ + import net.minecraft.init.Blocks; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.block.CraftBlockState; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++// CraftBukkit end ++ + public class ItemFlintAndSteel extends Item + { + private static final String __OBFID = "CL_00000035"; +@@ -19,6 +24,8 @@ + + public boolean onItemUse(ItemStack p_77648_1_, EntityPlayer p_77648_2_, World p_77648_3_, int p_77648_4_, int p_77648_5_, int p_77648_6_, int p_77648_7_, float p_77648_8_, float p_77648_9_, float p_77648_10_) + { ++ int clickedX = p_77648_4_, clickedY = p_77648_5_, clickedZ = p_77648_6_; // CraftBukkit ++ + if (p_77648_7_ == 0) + { + --p_77648_5_; +@@ -57,8 +64,27 @@ + { + if (p_77648_3_.isAirBlock(p_77648_4_, p_77648_5_, p_77648_6_)) + { ++ // CraftBukkit start - Store the clicked block ++ if (CraftEventFactory.callBlockIgniteEvent(p_77648_3_, p_77648_4_, p_77648_5_, p_77648_6_, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL, p_77648_2_).isCancelled()) ++ { ++ p_77648_1_.damageItem(1, p_77648_2_); ++ return false; ++ } ++ ++ CraftBlockState blockState = CraftBlockState.getBlockState(p_77648_3_, p_77648_4_, p_77648_5_, p_77648_6_); ++ // CraftBukkit end + p_77648_3_.playSoundEffect((double)p_77648_4_ + 0.5D, (double)p_77648_5_ + 0.5D, (double)p_77648_6_ + 0.5D, "fire.ignite", 1.0F, itemRand.nextFloat() * 0.4F + 0.8F); + p_77648_3_.setBlock(p_77648_4_, p_77648_5_, p_77648_6_, Blocks.fire); ++ // CraftBukkit start ++ org.bukkit.event.block.BlockPlaceEvent placeEvent = CraftEventFactory.callBlockPlaceEvent(p_77648_3_, p_77648_2_, blockState, clickedX, clickedY, clickedZ); ++ ++ if (placeEvent.isCancelled() || !placeEvent.canBuild()) ++ { ++ placeEvent.getBlockPlaced().setTypeIdAndData(0, (byte) 0, false); ++ return false; ++ } ++ ++ // CraftBukkit end + } + + p_77648_1_.damageItem(1, p_77648_2_); diff --git a/patches/net/minecraft/item/ItemHangingEntity.java.patch b/patches/net/minecraft/item/ItemHangingEntity.java.patch new file mode 100644 index 0000000..284e620 --- /dev/null +++ b/patches/net/minecraft/item/ItemHangingEntity.java.patch @@ -0,0 +1,44 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemHangingEntity.java ++++ ../src-work/minecraft/net/minecraft/item/ItemHangingEntity.java +@@ -8,6 +8,12 @@ + import net.minecraft.util.Direction; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.entity.Player; ++import org.bukkit.event.hanging.HangingPlaceEvent; ++import org.bukkit.event.painting.PaintingPlaceEvent; ++// CraftBukkit end ++ + public class ItemHangingEntity extends Item + { + private final Class hangingEntityClass; +@@ -44,6 +50,28 @@ + { + if (!p_77648_3_.isRemote) + { ++ // CraftBukkit start ++ Player who = (p_77648_2_ == null) ? null : (Player) p_77648_2_.getBukkitEntity(); ++ org.bukkit.block.Block blockClicked = p_77648_3_.getWorld().getBlockAt(p_77648_4_, p_77648_5_, p_77648_6_); ++ org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(p_77648_7_); ++ HangingPlaceEvent event = new HangingPlaceEvent((org.bukkit.entity.Hanging) entityhanging.getBukkitEntity(), who, blockClicked, blockFace); ++ p_77648_3_.getServer().getPluginManager().callEvent(event); ++ PaintingPlaceEvent paintingEvent = null; ++ ++ if (entityhanging instanceof EntityPainting) ++ { ++ // Fire old painting event until it can be removed ++ paintingEvent = new PaintingPlaceEvent((org.bukkit.entity.Painting) entityhanging.getBukkitEntity(), who, blockClicked, blockFace); ++ paintingEvent.setCancelled(event.isCancelled()); ++ p_77648_3_.getServer().getPluginManager().callEvent(paintingEvent); ++ } ++ ++ if (event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) ++ { ++ return false; ++ } ++ ++ // CraftBukkit end + p_77648_3_.spawnEntityInWorld(entityhanging); + } + diff --git a/patches/net/minecraft/item/ItemLead.java.patch b/patches/net/minecraft/item/ItemLead.java.patch new file mode 100644 index 0000000..864b756 --- /dev/null +++ b/patches/net/minecraft/item/ItemLead.java.patch @@ -0,0 +1,38 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemLead.java ++++ ../src-work/minecraft/net/minecraft/item/ItemLead.java +@@ -10,6 +10,8 @@ + import net.minecraft.util.AxisAlignedBB; + import net.minecraft.world.World; + ++import org.bukkit.event.hanging.HangingPlaceEvent; // CraftBukkit ++ + public class ItemLead extends Item + { + private static final String __OBFID = "CL_00000045"; +@@ -61,8 +63,26 @@ + if (entityleashknot == null) + { + entityleashknot = EntityLeashKnot.func_110129_a(p_150909_1_, p_150909_2_, p_150909_3_, p_150909_4_); ++ // CraftBukkit start ++ HangingPlaceEvent event = new HangingPlaceEvent((org.bukkit.entity.Hanging) entityleashknot.getBukkitEntity(), p_150909_0_ != null ? (org.bukkit.entity.Player) p_150909_0_.getBukkitEntity() : null, p_150909_1_.getWorld().getBlockAt(p_150909_2_, p_150909_3_, p_150909_4_), org.bukkit.block.BlockFace.SELF); ++ p_150909_1_.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ entityleashknot.setDead(); ++ return false; ++ } ++ ++ // CraftBukkit end + } + ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent(entityliving, entityleashknot, p_150909_0_).isCancelled()) ++ { ++ continue; ++ } ++ ++ // CraftBukkit end + entityliving.setLeashedToEntity(entityleashknot, true); + flag = true; + } diff --git a/patches/net/minecraft/item/ItemLilyPad.java.patch b/patches/net/minecraft/item/ItemLilyPad.java.patch new file mode 100644 index 0000000..818195c --- /dev/null +++ b/patches/net/minecraft/item/ItemLilyPad.java.patch @@ -0,0 +1,36 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemLilyPad.java ++++ ../src-work/minecraft/net/minecraft/item/ItemLilyPad.java +@@ -48,7 +48,17 @@ + { + // special case for handling block placement with water lilies + net.minecraftforge.common.util.BlockSnapshot blocksnapshot = net.minecraftforge.common.util.BlockSnapshot.getBlockSnapshot(p_77659_2_, i, j + 1, k); ++ // Cauldron start - special case for handling block placement with water lilies ++ org.bukkit.block.BlockState blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(p_77659_2_, i, j + 1, k); + p_77659_2_.setBlock(i, j + 1, k, Blocks.waterlily); ++ org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(p_77659_2_, ++ p_77659_3_, blockstate, i, j, k); ++ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) ++ { ++ blockstate.update(true, false); ++ return p_77659_1_; ++ } ++ // Cauldron end + if (net.minecraftforge.event.ForgeEventFactory.onPlayerBlockPlace(p_77659_3_, blocksnapshot, net.minecraftforge.common.util.ForgeDirection.UP).isCanceled()) + { + blocksnapshot.restore(true, false); +@@ -66,6 +76,15 @@ + } + } + ++ // Cauldron start ++ public boolean onItemUse(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, World par3World, int par4, int par5, int par6, int par7, float par8, ++ float par9, float par10) ++ { ++ cpw.mods.fml.relauncher.FMLRelaunchLog.info("onItemUse par1ItemStack = " + par1ItemStack); ++ return super.onItemUse(par1ItemStack, par2EntityPlayer, par3World, par4, par5, par6, par7, par8, par9, par10); ++ } ++ // Cauldron end ++ + @SideOnly(Side.CLIENT) + public int getColorFromItemStack(ItemStack p_82790_1_, int p_82790_2_) + { diff --git a/patches/net/minecraft/item/ItemMap.java.patch b/patches/net/minecraft/item/ItemMap.java.patch new file mode 100644 index 0000000..fc882d1 --- /dev/null +++ b/patches/net/minecraft/item/ItemMap.java.patch @@ -0,0 +1,36 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemMap.java ++++ ../src-work/minecraft/net/minecraft/item/ItemMap.java +@@ -19,6 +19,11 @@ + import net.minecraft.world.chunk.Chunk; + import net.minecraft.world.storage.MapData; + ++// CraftBukkit start ++import org.bukkit.Bukkit; ++import org.bukkit.event.server.MapInitializeEvent; ++// CraftBukkit end ++ + public class ItemMap extends ItemMapBase + { + private static final String __OBFID = "CL_00000047"; +@@ -60,6 +65,10 @@ + mapdata.dimension = p_77873_2_.provider.dimensionId; + mapdata.markDirty(); + p_77873_2_.setItemData(s, mapdata); ++ // CraftBukkit start ++ MapInitializeEvent event = new MapInitializeEvent(mapdata.mapView); ++ Bukkit.getServer().getPluginManager().callEvent(event); ++ // CraftBukkit end + } + + return mapdata; +@@ -279,6 +288,10 @@ + mapdata1.dimension = mapdata.dimension; + mapdata1.markDirty(); + p_77622_2_.setItemData("map_" + p_77622_1_.getItemDamage(), mapdata1); ++ // CraftBukkit start ++ MapInitializeEvent event = new MapInitializeEvent(mapdata1.mapView); ++ Bukkit.getServer().getPluginManager().callEvent(event); ++ // CraftBukkit end + } + } + diff --git a/patches/net/minecraft/item/ItemMinecart.java.patch b/patches/net/minecraft/item/ItemMinecart.java.patch new file mode 100644 index 0000000..f29389b --- /dev/null +++ b/patches/net/minecraft/item/ItemMinecart.java.patch @@ -0,0 +1,18 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemMinecart.java ++++ ../src-work/minecraft/net/minecraft/item/ItemMinecart.java +@@ -79,6 +79,15 @@ + { + if (!p_77648_3_.isRemote) + { ++ // CraftBukkit start - Minecarts ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(p_77648_2_, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, p_77648_4_, p_77648_5_, p_77648_6_, p_77648_7_, p_77648_1_); ++ ++ if (event.isCancelled()) ++ { ++ return false; ++ } ++ ++ // CraftBukkit end + EntityMinecart entityminecart = EntityMinecart.createMinecart(p_77648_3_, (double)((float)p_77648_4_ + 0.5F), (double)((float)p_77648_5_ + 0.5F), (double)((float)p_77648_6_ + 0.5F), this.minecartType); + + if (p_77648_1_.hasDisplayName()) diff --git a/patches/net/minecraft/item/ItemMonsterPlacer.java.patch b/patches/net/minecraft/item/ItemMonsterPlacer.java.patch new file mode 100644 index 0000000..b312855 --- /dev/null +++ b/patches/net/minecraft/item/ItemMonsterPlacer.java.patch @@ -0,0 +1,20 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemMonsterPlacer.java ++++ ../src-work/minecraft/net/minecraft/item/ItemMonsterPlacer.java +@@ -55,7 +55,7 @@ + + public boolean onItemUse(ItemStack p_77648_1_, EntityPlayer p_77648_2_, World p_77648_3_, int p_77648_4_, int p_77648_5_, int p_77648_6_, int p_77648_7_, float p_77648_8_, float p_77648_9_, float p_77648_10_) + { +- if (p_77648_3_.isRemote) ++ if (p_77648_3_.isRemote || p_77648_1_.getItemDamage() == 48 || p_77648_1_.getItemDamage() == 49 || p_77648_1_.getItemDamage() == 63 || p_77648_1_.getItemDamage() == 64) // CraftBukkit + { + return true; + } +@@ -168,7 +168,7 @@ + entityliving.rotationYawHead = entityliving.rotationYaw; + entityliving.renderYawOffset = entityliving.rotationYaw; + entityliving.onSpawnWithEgg((IEntityLivingData)null); +- p_77840_0_.spawnEntityInWorld(entity); ++ p_77840_0_.addEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // CraftBukkit + entityliving.playLivingSound(); + } + } diff --git a/patches/net/minecraft/item/ItemShears.java.patch b/patches/net/minecraft/item/ItemShears.java.patch new file mode 100644 index 0000000..51fa182 --- /dev/null +++ b/patches/net/minecraft/item/ItemShears.java.patch @@ -0,0 +1,28 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemShears.java ++++ ../src-work/minecraft/net/minecraft/item/ItemShears.java +@@ -3,6 +3,8 @@ + import java.util.ArrayList; + import java.util.Random; + ++import org.bukkit.event.player.PlayerShearEntityEvent; ++ + import net.minecraft.block.Block; + import net.minecraft.block.material.Material; + import net.minecraft.creativetab.CreativeTabs; +@@ -61,6 +63,16 @@ + IShearable target = (IShearable)entity; + if (target.isShearable(itemstack, entity.worldObj, (int)entity.posX, (int)entity.posY, (int)entity.posZ)) + { ++ // Cauldron start ++ PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) player.getBukkitEntity(), entity.getBukkitEntity()); ++ player.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return false; ++ } ++ ++ // Cauldron end + ArrayList drops = target.onSheared(itemstack, entity.worldObj, (int)entity.posX, (int)entity.posY, (int)entity.posZ, + EnchantmentHelper.getEnchantmentLevel(Enchantment.fortune.effectId, itemstack)); + diff --git a/patches/net/minecraft/item/ItemStack.java.patch b/patches/net/minecraft/item/ItemStack.java.patch new file mode 100644 index 0000000..b1fdb77 --- /dev/null +++ b/patches/net/minecraft/item/ItemStack.java.patch @@ -0,0 +1,227 @@ +--- ../src-base/minecraft/net/minecraft/item/ItemStack.java ++++ ../src-work/minecraft/net/minecraft/item/ItemStack.java +@@ -35,6 +35,20 @@ + import net.minecraft.world.World; + import net.minecraftforge.event.ForgeEventFactory; + ++import org.bukkit.craftbukkit.util.CraftMagicNumbers; // CraftBukkit ++import net.minecraft.entity.player.EntityPlayerMP; // Spigot ++// Cauldron start ++import net.minecraft.block.BlockSapling; ++import net.minecraft.block.BlockMushroom; ++ ++import org.bukkit.Location; ++import org.bukkit.TreeType; ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.block.CraftBlockState; ++import org.bukkit.entity.Player; ++import org.bukkit.event.world.StructureGrowEvent; ++// Cauldron end ++ + public final class ItemStack + { + public static final DecimalFormat field_111284_a = new DecimalFormat("#.###"); +@@ -43,7 +57,12 @@ + private Item field_151002_e; + public NBTTagCompound stackTagCompound; + int itemDamage; +- private EntityItemFrame itemFrame; ++ // Cauldron - due to a bug in Gson(https://code.google.com/p/google-gson/issues/detail?id=440), a stackoverflow ++ // can occur when gson attempts to resolve a field of a class that points to itself. ++ // As a temporary workaround, we will prevent serialization for this object until the bug is fixed. ++ // This fixes EE3's serialization of ItemStack. ++ private transient EntityItemFrame itemFrame; ++ public static EntityPlayer currentPlayer; // Cauldron - reference to current player calling onItemUse + private static final String __OBFID = "CL_00000043"; + + private cpw.mods.fml.common.registry.RegistryDelegate delegate; +@@ -126,12 +145,120 @@ + public boolean tryPlaceItemIntoWorld(EntityPlayer p_77943_1_, World p_77943_2_, int p_77943_3_, int p_77943_4_, int p_77943_5_, int p_77943_6_, float p_77943_7_, float p_77943_8_, float p_77943_9_) + { + if (!p_77943_2_.isRemote) return net.minecraftforge.common.ForgeHooks.onPlaceItemIntoWorld(this, p_77943_1_, p_77943_2_, p_77943_3_, p_77943_4_, p_77943_5_, p_77943_6_, p_77943_7_, p_77943_8_, p_77943_9_); ++ // Cauldron start - handle all placement events here ++ int meta = this.getItemDamage(); ++ int size = this.stackSize; ++ NBTTagCompound nbt = null; ++ if (this.getTagCompound() != null) ++ { ++ nbt = (NBTTagCompound) this.getTagCompound().copy(); ++ } ++ ++ if (!(this.getItem() instanceof ItemBucket)) // if not bucket ++ { ++ p_77943_2_.captureBlockStates = true; ++ if (this.getItem() instanceof ItemDye && this.getItemDamage() == 15) ++ { ++ Block block = p_77943_2_.getBlock(p_77943_3_, p_77943_4_, p_77943_5_); ++ if (block != null && (block instanceof BlockSapling || block instanceof BlockMushroom)) ++ { ++ p_77943_2_.captureTreeGeneration = true; ++ } ++ } ++ } ++ currentPlayer = p_77943_1_; + boolean flag = this.getItem().onItemUse(this, p_77943_1_, p_77943_2_, p_77943_3_, p_77943_4_, p_77943_5_, p_77943_6_, p_77943_7_, p_77943_8_, p_77943_9_); ++ p_77943_2_.captureBlockStates = false; ++ currentPlayer = null; ++ if (flag && p_77943_2_.captureTreeGeneration && p_77943_2_.capturedBlockStates.size() > 0) ++ { ++ p_77943_2_.captureTreeGeneration = false; ++ Location location = new Location(p_77943_2_.getWorld(), p_77943_3_, p_77943_4_, p_77943_5_); ++ TreeType treeType = BlockSapling.treeType; ++ BlockSapling.treeType = null; ++ List blocks = (List) p_77943_2_.capturedBlockStates.clone(); ++ p_77943_2_.capturedBlockStates.clear(); ++ StructureGrowEvent event = null; ++ if (treeType != null) ++ { ++ event = new StructureGrowEvent(location, treeType, false, (Player) p_77943_1_.getBukkitEntity(), blocks); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ } ++ if (event == null || !event.isCancelled()) ++ { ++ for (BlockState blockstate : blocks) ++ { ++ blockstate.update(true); ++ } ++ } + ++ return flag; ++ } ++ p_77943_2_.captureTreeGeneration = false; ++ + if (flag) + { +- p_77943_1_.addStat(StatList.objectUseStats[Item.getIdFromItem(this.field_151002_e)], 1); ++ org.bukkit.event.block.BlockPlaceEvent placeEvent = null; ++ List blockstates = (List) p_77943_2_.capturedBlockStates.clone(); ++ p_77943_2_.capturedBlockStates.clear(); ++ if (blockstates.size() > 1) ++ { ++ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(p_77943_2_, p_77943_1_, blockstates, p_77943_3_, ++ p_77943_4_, p_77943_5_); ++ } ++ else if (blockstates.size() == 1) ++ { ++ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(p_77943_2_, p_77943_1_, blockstates.get(0), p_77943_3_, ++ p_77943_4_, p_77943_5_); ++ } ++ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) ++ { ++ flag = false; // cancel placement ++ // revert back all captured blocks ++ for (BlockState blockstate : blockstates) ++ { ++ p_77943_2_.restoringBlockStates = true; ++ blockstate.update(true, false); // restore blockstate ++ p_77943_2_.restoringBlockStates = false; ++ } ++ // make sure to restore stack after cancel ++ this.setItemDamage(meta); ++ this.stackSize = size; ++ if (nbt != null) ++ { ++ this.setTagCompound(nbt); ++ } ++ } ++ else ++ { ++ // drop items ++ for (int i = 0; i < p_77943_2_.capturedItems.size(); i++) ++ { ++ p_77943_2_.spawnEntityInWorld(p_77943_2_.capturedItems.get(i)); ++ } ++ ++ for (BlockState blockstate : blockstates) ++ { ++ int x = blockstate.getX(); ++ int y = blockstate.getY(); ++ int z = blockstate.getZ(); ++ int metadata = p_77943_2_.getBlockMetadata(x, y, z); ++ int updateFlag = ((CraftBlockState) blockstate).getFlag(); ++ Block oldBlock = CraftMagicNumbers.getBlock(blockstate.getTypeId()); ++ Block newBlock = p_77943_2_.getBlock(x, y, z); ++ if (newBlock != null && !(newBlock.hasTileEntity(metadata))) ++ { // Containers get placed automatically ++ newBlock.onBlockAdded(p_77943_2_, x, y, z); ++ } ++ ++ p_77943_2_.markAndNotifyBlock(x, y, z, null, oldBlock, newBlock, updateFlag); ++ } ++ p_77943_1_.addStat(StatList.objectUseStats[Item.getIdFromItem(this.field_151002_e)], 1); ++ } + } ++ p_77943_2_.capturedBlockStates.clear(); ++ p_77943_2_.capturedItems.clear(); ++ // Cauldron end + + return flag; + } +@@ -227,8 +354,22 @@ + return getItem().getMaxDamage(this); + } + ++ // Spigot start ++ ++ /** ++ * Attempts to damage the ItemStack with par1 amount of damage, If the ItemStack has the Unbreaking enchantment ++ * there is a chance for each point of damage to be negated. Returns true if it takes more damage than ++ * getMaxDamage(). Returns false otherwise or if the ItemStack can't be damaged or if all points of damage are ++ * negated. ++ */ + public boolean attemptDamageItem(int p_96631_1_, Random p_96631_2_) + { ++ return isDamaged(p_96631_1_, p_96631_2_, null); ++ } ++ ++ public boolean isDamaged(int p_96631_1_, Random p_96631_2_, EntityLivingBase entitylivingbase) ++ { ++ // Spigot end + if (!this.isItemStackDamageable()) + { + return false; +@@ -250,6 +391,23 @@ + + p_96631_1_ -= k; + ++ // Spigot start ++ if (entitylivingbase instanceof EntityPlayerMP) ++ { ++ org.bukkit.craftbukkit.inventory.CraftItemStack item = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this); ++ org.bukkit.event.player.PlayerItemDamageEvent event = new org.bukkit.event.player.PlayerItemDamageEvent( ++ (org.bukkit.entity.Player) entitylivingbase.getBukkitEntity(), item, p_96631_1_); ++ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return false; ++ } ++ ++ p_96631_1_ = event.getDamage(); ++ } ++ // Spigot end ++ + if (p_96631_1_ <= 0) + { + return false; +@@ -288,6 +446,12 @@ + this.stackSize = 0; + } + ++ // CraftBukkit start - Check for item breaking ++ if (this.stackSize == 0 && p_77972_2_ instanceof EntityPlayer) ++ { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent((EntityPlayer) p_77972_2_, this); ++ } ++ // CraftBukkit end + this.itemDamage = 0; + } + } +@@ -419,6 +583,7 @@ + + public void setTagCompound(NBTTagCompound p_77982_1_) + { ++ // Cauldron - do not alter name of compound. Fixes Ars Magica 2 Spellbooks + this.stackTagCompound = p_77982_1_; + } + diff --git a/patches/net/minecraft/item/crafting/CraftingManager.java.patch b/patches/net/minecraft/item/crafting/CraftingManager.java.patch new file mode 100644 index 0000000..7b34355 --- /dev/null +++ b/patches/net/minecraft/item/crafting/CraftingManager.java.patch @@ -0,0 +1,104 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/CraftingManager.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/CraftingManager.java +@@ -13,10 +13,17 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.world.World; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class CraftingManager + { + private static final CraftingManager instance = new CraftingManager(); +- private List recipes = new ArrayList(); ++ // CraftBukkit start ++ /** A list of all the recipes added */ ++ public List recipes = new ArrayList(); // private -> public ++ public IRecipe lastRecipe; ++ public org.bukkit.inventory.InventoryView lastCraftView; ++ // CraftBukkit end + private static final String __OBFID = "CL_00000090"; + + public static final CraftingManager getInstance() +@@ -24,7 +31,8 @@ + return instance; + } + +- private CraftingManager() ++ // CraftBukkit - private -> public ++ public CraftingManager() + { + (new RecipesTools()).addRecipes(this); + (new RecipesWeapons()).addRecipes(this); +@@ -152,6 +160,23 @@ + this.addShapelessRecipe(new ItemStack(Items.fire_charge, 3), new Object[] {Items.gunpowder, Items.blaze_powder, new ItemStack(Items.coal, 1, 1)}); + this.addRecipe(new ItemStack(Blocks.daylight_detector), new Object[] {"GGG", "QQQ", "WWW", 'G', Blocks.glass, 'Q', Items.quartz, 'W', Blocks.wooden_slab}); + this.addRecipe(new ItemStack(Blocks.hopper), new Object[] {"I I", "ICI", " I ", 'I', Items.iron_ingot, 'C', Blocks.chest}); ++ /*Collections.sort(this.recipes, new Comparator() // CraftBukkit - moved below ++ { ++ private static final String __OBFID = "CL_00000091"; ++ public int compare(IRecipe par1IRecipe, IRecipe par2IRecipe) ++ { ++ return par1IRecipe instanceof ShapelessRecipes && par2IRecipe instanceof ShapedRecipes ? 1 : (par2IRecipe instanceof ShapelessRecipes && par1IRecipe instanceof ShapedRecipes ? -1 : (par2IRecipe.getRecipeSize() < par1IRecipe.getRecipeSize() ? -1 : (par2IRecipe.getRecipeSize() > par1IRecipe.getRecipeSize() ? 1 : 0))); ++ } ++ public int compare(Object par1Obj, Object par2Obj) ++ { ++ return this.compare((IRecipe)par1Obj, (IRecipe)par2Obj); ++ } ++ });*/ ++ this.sort(); // CraftBukkit - call new sort method ++ } ++ ++ public void sort() ++ { + Collections.sort(this.recipes, new Comparator() + { + private static final String __OBFID = "CL_00000091"; +@@ -312,7 +337,22 @@ + i1 = 0; + } + +- return new ItemStack(itemstack.getItem(), 1, i1); ++ // Cauldron start - vanilla compatibility ++ if (p_82787_1_.resultInventory == null) ++ { ++ return new ItemStack(itemstack.getItem(), 1, i1); ++ } ++ // Cauldron end ++ // CraftBukkit start - Construct a dummy repair recipe ++ ItemStack result = new ItemStack(itemstack.getItem(), 1, i1); ++ List ingredients = new ArrayList(); ++ ingredients.add(itemstack.copy()); ++ ingredients.add(itemstack1.copy()); ++ ShapelessRecipes recipe = new ShapelessRecipes(result.copy(), ingredients); ++ p_82787_1_.currentRecipe = recipe; ++ result = CraftEventFactory.callPreCraftEvent(p_82787_1_, result, lastCraftView, true); ++ return result; ++ // CraftBukkit end + } + else + { +@@ -320,12 +360,23 @@ + { + IRecipe irecipe = (IRecipe)this.recipes.get(j); + +- if (irecipe.matches(p_82787_1_, p_82787_2_)) ++ if (irecipe.matches(p_82787_1_, p_82787_2_) && p_82787_1_.resultInventory != null) // Cauldron - add null check for vanilla compatibility + { ++ // CraftBukkit start - INVENTORY_PRE_CRAFT event ++ p_82787_1_.currentRecipe = irecipe; ++ ItemStack result = irecipe.getCraftingResult(p_82787_1_); ++ return CraftEventFactory.callPreCraftEvent(p_82787_1_, result, lastCraftView, false); ++ // CraftBukkit end ++ } ++ // Cauldron start - vanilla ++ else if (irecipe.matches(p_82787_1_, p_82787_2_)) ++ { + return irecipe.getCraftingResult(p_82787_1_); + } ++ // Cauldron end + } + ++ p_82787_1_.currentRecipe = null; // CraftBukkit - Clear recipe when no recipe is found + return null; + } + } diff --git a/patches/net/minecraft/item/crafting/FurnaceRecipes.java.patch b/patches/net/minecraft/item/crafting/FurnaceRecipes.java.patch new file mode 100644 index 0000000..df5135d --- /dev/null +++ b/patches/net/minecraft/item/crafting/FurnaceRecipes.java.patch @@ -0,0 +1,62 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/FurnaceRecipes.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/FurnaceRecipes.java +@@ -14,8 +14,9 @@ + public class FurnaceRecipes + { + private static final FurnaceRecipes smeltingBase = new FurnaceRecipes(); +- private Map smeltingList = new HashMap(); ++ public Map smeltingList = new HashMap(); // CraftBukkit - private -> public + private Map experienceList = new HashMap(); ++ public Map customRecipes = new HashMap(); // CraftBukkit + private static final String __OBFID = "CL_00000085"; + + public static FurnaceRecipes smelting() +@@ -23,7 +24,7 @@ + return smeltingBase; + } + +- private FurnaceRecipes() ++ public FurnaceRecipes() // CraftBukkit - private -> public + { + this.func_151393_a(Blocks.iron_ore, new ItemStack(Items.iron_ingot), 0.7F); + this.func_151393_a(Blocks.gold_ore, new ItemStack(Items.gold_ingot), 1.0F); +@@ -76,16 +77,37 @@ + this.experienceList.put(p_151394_2_, Float.valueOf(p_151394_3_)); + } + ++ // CraftBukkit start ++ public void registerRecipe(ItemStack itemstack, ItemStack itemstack1) ++ { ++ this.customRecipes.put(itemstack, itemstack1); ++ } ++ // CraftBukkit end ++ + public ItemStack getSmeltingResult(ItemStack p_151395_1_) + { +- Iterator iterator = this.smeltingList.entrySet().iterator(); ++ // CraftBukkit start ++ boolean vanilla = false; ++ Iterator iterator = this.customRecipes.entrySet().iterator(); ++ // CraftBukkit end + Entry entry; + + do + { + if (!iterator.hasNext()) + { +- return null; ++ // CraftBukkit start ++ if (!vanilla) ++ { ++ iterator = this.smeltingList.entrySet().iterator(); ++ vanilla = true; ++ } ++ else ++ { ++ return null; ++ } ++ ++ // CraftBukkit end + } + + entry = (Entry)iterator.next(); diff --git a/patches/net/minecraft/item/crafting/IRecipe.java.patch b/patches/net/minecraft/item/crafting/IRecipe.java.patch new file mode 100644 index 0000000..285ab14 --- /dev/null +++ b/patches/net/minecraft/item/crafting/IRecipe.java.patch @@ -0,0 +1,9 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/IRecipe.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/IRecipe.java +@@ -13,4 +13,6 @@ + int getRecipeSize(); + + ItemStack getRecipeOutput(); ++ ++ org.bukkit.inventory.Recipe toBukkitRecipe(); // CraftBukkit + } diff --git a/patches/net/minecraft/item/crafting/RecipeBookCloning.java.patch b/patches/net/minecraft/item/crafting/RecipeBookCloning.java.patch new file mode 100644 index 0000000..a0f3299 --- /dev/null +++ b/patches/net/minecraft/item/crafting/RecipeBookCloning.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/RecipeBookCloning.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/RecipeBookCloning.java +@@ -6,10 +6,17 @@ + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.world.World; + +-public class RecipeBookCloning implements IRecipe ++public class RecipeBookCloning extends ShapelessRecipes implements IRecipe // CraftBukkit - added extends + { + private static final String __OBFID = "CL_00000081"; + ++ // CraftBukkit start - Delegate to new parent class ++ public RecipeBookCloning() ++ { ++ super(new ItemStack(Items.written_book, 0, -1), java.util.Arrays.asList(new ItemStack(Items.writable_book, 0, 0))); ++ } ++ // CraftBukkit end ++ + public boolean matches(InventoryCrafting p_77569_1_, World p_77569_2_) + { + int i = 0; diff --git a/patches/net/minecraft/item/crafting/RecipeFireworks.java.patch b/patches/net/minecraft/item/crafting/RecipeFireworks.java.patch new file mode 100644 index 0000000..eb6d629 --- /dev/null +++ b/patches/net/minecraft/item/crafting/RecipeFireworks.java.patch @@ -0,0 +1,22 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/RecipeFireworks.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/RecipeFireworks.java +@@ -9,11 +9,18 @@ + import net.minecraft.nbt.NBTTagList; + import net.minecraft.world.World; + +-public class RecipeFireworks implements IRecipe ++public class RecipeFireworks extends ShapelessRecipes implements IRecipe // CraftBukkit - added extends + { + private ItemStack field_92102_a; + private static final String __OBFID = "CL_00000083"; + ++ // CraftBukkit start - Delegate to new parent class with bogus info ++ public RecipeFireworks() ++ { ++ super(new ItemStack(Items.fireworks, 0, 0), java.util.Arrays.asList(new ItemStack(Items.gunpowder, 0, 5))); ++ } ++ // CraftBukkit end ++ + public boolean matches(InventoryCrafting p_77569_1_, World p_77569_2_) + { + this.field_92102_a = null; diff --git a/patches/net/minecraft/item/crafting/RecipesArmorDyes.java.patch b/patches/net/minecraft/item/crafting/RecipesArmorDyes.java.patch new file mode 100644 index 0000000..b069d80 --- /dev/null +++ b/patches/net/minecraft/item/crafting/RecipesArmorDyes.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/RecipesArmorDyes.java +@@ -9,10 +9,17 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.world.World; + +-public class RecipesArmorDyes implements IRecipe ++public class RecipesArmorDyes extends ShapelessRecipes implements IRecipe // CraftBukkit - added extends + { + private static final String __OBFID = "CL_00000079"; + ++ // CraftBukkit start - Delegate to new parent class with bogus info ++ public RecipesArmorDyes() ++ { ++ super(new ItemStack(Items.leather_helmet, 0, 0), java.util.Arrays.asList(new ItemStack(Items.dye, 0, 5))); ++ } ++ // CraftBukkit end ++ + public boolean matches(InventoryCrafting p_77569_1_, World p_77569_2_) + { + ItemStack itemstack = null; diff --git a/patches/net/minecraft/item/crafting/RecipesMapCloning.java.patch b/patches/net/minecraft/item/crafting/RecipesMapCloning.java.patch new file mode 100644 index 0000000..70c8e8e --- /dev/null +++ b/patches/net/minecraft/item/crafting/RecipesMapCloning.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/RecipesMapCloning.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/RecipesMapCloning.java +@@ -5,10 +5,17 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.world.World; + +-public class RecipesMapCloning implements IRecipe ++public class RecipesMapCloning extends ShapelessRecipes implements IRecipe // CraftBukkit - added extends + { + private static final String __OBFID = "CL_00000087"; + ++ // CraftBukkit start - Delegate to new parent class ++ public RecipesMapCloning() ++ { ++ super(new ItemStack(Items.filled_map, 0, -1), java.util.Arrays.asList(new ItemStack(Items.map, 0, 0))); ++ } ++ // CraftBukkit end ++ + public boolean matches(InventoryCrafting p_77569_1_, World p_77569_2_) + { + int i = 0; diff --git a/patches/net/minecraft/item/crafting/ShapedRecipes.java.patch b/patches/net/minecraft/item/crafting/ShapedRecipes.java.patch new file mode 100644 index 0000000..e1baadf --- /dev/null +++ b/patches/net/minecraft/item/crafting/ShapedRecipes.java.patch @@ -0,0 +1,92 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/ShapedRecipes.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/ShapedRecipes.java +@@ -5,6 +5,11 @@ + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; ++// CraftBukkit end ++ + public class ShapedRecipes implements IRecipe + { + public final int recipeWidth; +@@ -22,6 +27,77 @@ + this.recipeOutput = p_i1917_4_; + } + ++ // CraftBukkit start ++ public org.bukkit.inventory.ShapedRecipe toBukkitRecipe() ++ { ++ CraftItemStack result = CraftItemStack.asCraftMirror(this.recipeOutput); ++ CraftShapedRecipe recipe = new CraftShapedRecipe(result, this); ++ ++ switch (this.recipeHeight) ++ { ++ case 1: ++ switch (this.recipeWidth) ++ { ++ case 1: ++ recipe.shape("a"); ++ break; ++ case 2: ++ recipe.shape("ab"); ++ break; ++ case 3: ++ recipe.shape("abc"); ++ break; ++ } ++ ++ break; ++ case 2: ++ switch (this.recipeWidth) ++ { ++ case 1: ++ recipe.shape("a", "b"); ++ break; ++ case 2: ++ recipe.shape("ab", "cd"); ++ break; ++ case 3: ++ recipe.shape("abc", "def"); ++ break; ++ } ++ ++ break; ++ case 3: ++ switch (this.recipeWidth) ++ { ++ case 1: ++ recipe.shape("a", "b", "c"); ++ break; ++ case 2: ++ recipe.shape("ab", "cd", "ef"); ++ break; ++ case 3: ++ recipe.shape("abc", "def", "ghi"); ++ break; ++ } ++ ++ break; ++ } ++ ++ char c = 'a'; ++ ++ for (ItemStack stack : this.recipeItems) ++ { ++ if (stack != null) ++ { ++ recipe.setIngredient(c, org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(stack.getItem()), stack.getItemDamage()); ++ } ++ ++ c++; ++ } ++ ++ return recipe; ++ } ++ // CraftBukkit end ++ + public ItemStack getRecipeOutput() + { + return this.recipeOutput; diff --git a/patches/net/minecraft/item/crafting/ShapelessRecipes.java.patch b/patches/net/minecraft/item/crafting/ShapelessRecipes.java.patch new file mode 100644 index 0000000..874586f --- /dev/null +++ b/patches/net/minecraft/item/crafting/ShapelessRecipes.java.patch @@ -0,0 +1,40 @@ +--- ../src-base/minecraft/net/minecraft/item/crafting/ShapelessRecipes.java ++++ ../src-work/minecraft/net/minecraft/item/crafting/ShapelessRecipes.java +@@ -7,6 +7,11 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.world.World; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; ++// CraftBukkit end ++ + public class ShapelessRecipes implements IRecipe + { + private final ItemStack recipeOutput; +@@ -19,6 +24,25 @@ + this.recipeItems = p_i1918_2_; + } + ++ // CraftBukkit start ++ @SuppressWarnings("unchecked") ++ public org.bukkit.inventory.ShapelessRecipe toBukkitRecipe() ++ { ++ CraftItemStack result = CraftItemStack.asCraftMirror(this.recipeOutput); ++ CraftShapelessRecipe recipe = new CraftShapelessRecipe(result, this); ++ ++ for (ItemStack stack : (List) this.recipeItems) ++ { ++ if (stack != null) ++ { ++ recipe.addIngredient(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(stack.getItem()), stack.getItemDamage()); ++ } ++ } ++ ++ return recipe; ++ } ++ // CraftBukkit end ++ + public ItemStack getRecipeOutput() + { + return this.recipeOutput; diff --git a/patches/net/minecraft/network/NetHandlerPlayServer.java.patch b/patches/net/minecraft/network/NetHandlerPlayServer.java.patch new file mode 100644 index 0000000..5e24391 --- /dev/null +++ b/patches/net/minecraft/network/NetHandlerPlayServer.java.patch @@ -0,0 +1,1940 @@ +--- ../src-base/minecraft/net/minecraft/network/NetHandlerPlayServer.java ++++ ../src-work/minecraft/net/minecraft/network/NetHandlerPlayServer.java +@@ -13,6 +13,7 @@ + import java.util.Iterator; + import java.util.Random; + import java.util.concurrent.Callable; ++import net.minecraft.block.Block; + import net.minecraft.block.material.Material; + import net.minecraft.command.server.CommandBlockLogic; + import net.minecraft.crash.CrashReport; +@@ -32,6 +33,7 @@ + import net.minecraft.inventory.ContainerMerchant; + import net.minecraft.inventory.ContainerRepair; + import net.minecraft.inventory.Slot; ++import net.minecraft.item.Item; + import net.minecraft.item.ItemEditableBook; + import net.minecraft.item.ItemStack; + import net.minecraft.item.ItemWritableBook; +@@ -61,6 +63,7 @@ + import net.minecraft.network.play.server.S00PacketKeepAlive; + import net.minecraft.network.play.server.S02PacketChat; + import net.minecraft.network.play.server.S08PacketPlayerPosLook; ++import net.minecraft.network.play.server.S1CPacketEntityMetadata; + import net.minecraft.network.play.server.S23PacketBlockChange; + import net.minecraft.network.play.server.S2FPacketSetSlot; + import net.minecraft.network.play.server.S32PacketConfirmTransaction; +@@ -81,19 +84,75 @@ + import net.minecraft.util.IChatComponent; + import net.minecraft.util.IntHashMap; + import net.minecraft.util.ReportedException; ++import net.minecraft.world.World; + import net.minecraft.world.WorldServer; + import org.apache.commons.lang3.StringUtils; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import net.minecraftforge.cauldron.CauldronUtils; + import net.minecraftforge.common.ForgeHooks; + import net.minecraftforge.common.MinecraftForge; +-import cpw.mods.fml.common.eventhandler.Event; + import net.minecraftforge.event.ForgeEventFactory; + import net.minecraftforge.event.ServerChatEvent; + import net.minecraftforge.event.entity.player.PlayerInteractEvent; +-import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action; + ++// CraftBukkit start ++import java.io.UnsupportedEncodingException; ++import java.util.concurrent.ExecutionException; ++import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; ++import java.util.HashSet; ++ ++import net.minecraft.entity.EntityLiving; ++import net.minecraft.init.Blocks; ++import net.minecraft.network.play.server.S05PacketSpawnPosition; ++import net.minecraft.network.play.server.S09PacketHeldItemChange; ++import net.minecraft.network.play.server.S1BPacketEntityAttach; ++import net.minecraft.network.play.server.S33PacketUpdateSign; ++import net.minecraft.util.MathHelper; ++import net.minecraft.util.MovingObjectPosition; ++import net.minecraft.util.Vec3; ++ ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.util.CraftChatMessage; ++import org.bukkit.craftbukkit.util.LazyPlayerSet; ++import org.bukkit.craftbukkit.util.Waitable; ++ ++import org.bukkit.Location; ++import org.bukkit.entity.Player; ++import org.bukkit.event.Event; ++import org.bukkit.event.block.Action; ++import org.bukkit.event.block.SignChangeEvent; ++import org.bukkit.event.inventory.ClickType; ++import org.bukkit.event.inventory.CraftItemEvent; ++import org.bukkit.event.inventory.InventoryAction; ++import org.bukkit.event.inventory.InventoryClickEvent; ++import org.bukkit.event.inventory.InventoryCreativeEvent; ++import org.bukkit.event.inventory.InventoryType.SlotType; ++import org.bukkit.event.player.AsyncPlayerChatEvent; ++import org.bukkit.event.player.PlayerAnimationEvent; ++import org.bukkit.event.player.PlayerChatEvent; ++import org.bukkit.event.player.PlayerCommandPreprocessEvent; ++import org.bukkit.event.player.PlayerInteractEntityEvent; ++import org.bukkit.event.player.PlayerItemHeldEvent; ++import org.bukkit.event.player.PlayerKickEvent; ++import org.bukkit.event.player.PlayerMoveEvent; ++import org.bukkit.event.player.PlayerTeleportEvent; ++import org.bukkit.event.player.PlayerToggleFlightEvent; ++import org.bukkit.event.player.PlayerToggleSneakEvent; ++import org.bukkit.event.player.PlayerToggleSprintEvent; ++import org.bukkit.inventory.CraftingInventory; ++import org.bukkit.inventory.InventoryView; ++import org.bukkit.util.NumberConversions; ++// CraftBukkit end ++// Cauldron start ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.event.inventory.InventoryType; ++// Cauldron end ++ + public class NetHandlerPlayServer implements INetHandlerPlayServer + { + private static final Logger logger = LogManager.getLogger(); +@@ -107,13 +166,12 @@ + private long field_147379_i; + private static Random field_147376_j = new Random(); + private long field_147377_k; +- private int chatSpamThresholdCount; ++ private volatile int chatSpamThresholdCount; // Cauldron - set to volatile to fix multithreaded issues ++ private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(NetHandlerPlayServer.class, CauldronUtils.deobfuscatedEnvironment() ? "chatSpamThresholdCount" : "fiel" + "d_147374_l"); // CraftBukkit - multithreaded field + private int field_147375_m; + private IntHashMap field_147372_n = new IntHashMap(); +- private double lastPosX; +- private double lastPosY; +- private double lastPosZ; +- private boolean hasMoved = true; ++ public boolean hasMoved = true; // CraftBukkit - private -> public ++ private boolean processedDisconnect; // CraftBukkit - added + private static final String __OBFID = "CL_00001452"; + + public NetHandlerPlayServer(MinecraftServer p_i1530_1_, NetworkManager p_i1530_2_, EntityPlayerMP p_i1530_3_) +@@ -123,8 +181,41 @@ + p_i1530_2_.setNetHandler(this); + this.playerEntity = p_i1530_3_; + p_i1530_3_.playerNetServerHandler = this; ++ // CraftBukkit start ++ this.server = p_i1530_1_.server; + } + ++ private final org.bukkit.craftbukkit.CraftServer server; ++ private int lastTick = MinecraftServer.currentTick; ++ private int lastDropTick = MinecraftServer.currentTick; ++ private int dropCount = 0; ++ private static final int SURVIVAL_PLACE_DISTANCE_SQUARED = 6 * 6; ++ private static final int CREATIVE_PLACE_DISTANCE_SQUARED = 7 * 7; ++ ++ private double lastPosX = Double.MAX_VALUE; ++ private double lastPosY = Double.MAX_VALUE; ++ private double lastPosZ = Double.MAX_VALUE; ++ private float lastPitch = Float.MAX_VALUE; ++ private float lastYaw = Float.MAX_VALUE; ++ private boolean justTeleported = false; ++ ++ // For the PacketPlayOutBlockPlace hack :( ++ Long lastPacket; ++ ++ // Store the last block right clicked and what type it was ++ private Item lastMaterial; ++ ++ // Cauldron - rename getPlayer -> getPlayerB() to disambiguate with FML's getPlayer() method of the same name (below) ++ // Plugins calling this method will be remapped appropriately, but CraftBukkit code should be updated ++ public CraftPlayer getPlayerB() ++ { ++ return (this.playerEntity == null) ? null : (CraftPlayer) this.playerEntity.getBukkitEntity(); ++ } ++ ++ private final static HashSet invalidItems = new HashSet(java.util.Arrays.asList(8, 9, 10, 11, 26, 34, 36, 43, 51, 52, 55, 59, 60, 62, 63, ++ 64, 68, 71, 74, 75, 83, 90, 92, 93, 94, 104, 105, 115, 117, 118, 119, 125, 127, 132, 137, 140, 141, 142, 144)); // TODO: Check after every update. ++ // CraftBukkit end ++ + public void onNetworkTick() + { + this.field_147366_g = false; +@@ -139,10 +230,16 @@ + this.sendPacket(new S00PacketKeepAlive(this.field_147378_h)); + } + ++ // CraftBukkit start ++ for (int spam; (spam = this.chatSpamThresholdCount) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1);) ; ++ ++ /* Use thread-safe field access instead + if (this.chatSpamThresholdCount > 0) + { + --this.chatSpamThresholdCount; + } ++ */ ++ // CraftBukkit end + + if (this.field_147375_m > 0) + { +@@ -162,6 +259,24 @@ + + public void kickPlayerFromServer(String p_147360_1_) + { ++ // CraftBukkit start ++ String leaveMessage = EnumChatFormatting.YELLOW + this.playerEntity.getCommandSenderName() + " left the game."; ++ PlayerKickEvent event = new PlayerKickEvent(this.server.getPlayer(this.playerEntity), p_147360_1_, leaveMessage); ++ ++ if (this.server.getServer().isServerRunning()) ++ { ++ this.server.getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) ++ { ++ // Do not kick the player ++ return; ++ } ++ ++ // Send the possibly modified leave message ++ p_147360_1_ = event.getReason(); ++ // CraftBukkit end + final ChatComponentText chatcomponenttext = new ChatComponentText(p_147360_1_); + this.netManager.scheduleOutboundPacket(new S40PacketDisconnect(chatcomponenttext), new GenericFutureListener[] {new GenericFutureListener() + { +@@ -170,8 +285,8 @@ + { + NetHandlerPlayServer.this.netManager.closeChannel(chatcomponenttext); + } +- } +- }); ++ }}); ++ this.onDisconnect(chatcomponenttext); // CraftBukkit - Process quit immediately + this.netManager.disableAutoRead(); + } + +@@ -182,6 +297,15 @@ + + public void processPlayer(C03PacketPlayer p_147347_1_) + { ++ // CraftBukkit start - Check for NaN ++ if (Double.isNaN(p_147347_1_.field_149479_a) || Double.isNaN(p_147347_1_.field_149477_b) || Double.isNaN(p_147347_1_.field_149478_c) ++ || Double.isNaN(p_147347_1_.field_149475_d)) ++ { ++ logger.warn(playerEntity.getCommandSenderName() + " was caught trying to crash the server with an invalid position."); ++ getPlayerB().kickPlayer("Nope!"); ++ return; ++ } ++ // CraftBukkit end + WorldServer worldserver = this.serverController.worldServerForDimension(this.playerEntity.dimension); + this.field_147366_g = true; + +@@ -199,8 +323,78 @@ + } + } + +- if (this.hasMoved) ++ // CraftBukkit start ++ Player player = this.getPlayerB(); ++ Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location. ++ Location to = player.getLocation().clone(); // Start off the To location as the Players current location. ++ ++ // If the packet contains movement information then we update the To location with the correct XYZ. ++ if (p_147347_1_.field_149480_h && !(p_147347_1_.field_149480_h && p_147347_1_.field_149477_b == -999.0D && p_147347_1_.field_149475_d == -999.0D)) + { ++ to.setX(p_147347_1_.field_149479_a); ++ to.setY(p_147347_1_.field_149477_b); ++ to.setZ(p_147347_1_.field_149478_c); ++ } ++ ++ // If the packet contains look information then we update the To location with the correct Yaw & Pitch. ++ if (p_147347_1_.field_149481_i) ++ { ++ to.setYaw(p_147347_1_.field_149476_e); ++ to.setPitch(p_147347_1_.field_149473_f); ++ } ++ ++ // Prevent 40 event-calls for less than a single pixel of movement >.> ++ double delta = Math.pow(this.lastPosX - to.getX(), 2) + Math.pow(this.lastPosY - to.getY(), 2) + Math.pow(this.lastPosZ - to.getZ(), 2); ++ float deltaAngle = Math.abs(this.lastYaw - to.getYaw()) + Math.abs(this.lastPitch - to.getPitch()); ++ ++ if ((delta > 1f / 256 || deltaAngle > 10f) && (this.hasMoved && !this.playerEntity.isDead)) ++ { ++ this.lastPosX = to.getX(); ++ this.lastPosY = to.getY(); ++ this.lastPosZ = to.getZ(); ++ this.lastYaw = to.getYaw(); ++ this.lastPitch = to.getPitch(); ++ ++ // Skip the first time we do this ++ if (hasMoved) // Spigot - Better Check! ++ { ++ PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); ++ this.server.getPluginManager().callEvent(event); ++ ++ // If the event is cancelled we move the player back to their old location. ++ if (event.isCancelled()) ++ { ++ this.playerEntity.playerNetServerHandler.sendPacket(new S08PacketPlayerPosLook(from.getX(), from.getY() + 1.6200000047683716D, from ++ .getZ(), from.getYaw(), from.getPitch(), false)); ++ return; ++ } ++ ++ /* If a Plugin has changed the To destination then we teleport the Player ++ there to avoid any 'Moved wrongly' or 'Moved too quickly' errors. ++ We only do this if the Event was not cancelled. */ ++ if (!to.equals(event.getTo()) && !event.isCancelled()) ++ { ++ this.playerEntity.getBukkitEntity().teleport(event.getTo(), PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ return; ++ } ++ ++ /* Check to see if the Players Location has some how changed during the call of the event. ++ This can happen due to a plugin teleporting the player instead of using .setTo() */ ++ if (!from.equals(this.getPlayerB().getLocation()) && this.justTeleported) ++ { ++ this.justTeleported = false; ++ return; ++ } ++ } ++ else ++ { ++ hasMoved = true; // Spigot - Better Check! ++ } ++ } ++ ++ if (this.hasMoved && !this.playerEntity.isDead) ++ { ++ // CraftBukkit end + double d1; + double d2; + double d3; +@@ -318,7 +512,7 @@ + double d9 = Math.max(Math.abs(d6), Math.abs(this.playerEntity.motionZ)); + double d10 = d7 * d7 + d8 * d8 + d9 * d9; + +- if (d10 > 100.0D && (!this.serverController.isSinglePlayer() || !this.serverController.getServerOwner().equals(this.playerEntity.getCommandSenderName()))) ++ if (d10 > 100.0D && this.hasMoved && (!this.serverController.isSinglePlayer() || !this.serverController.getServerOwner().equals(this.playerEntity.getCommandSenderName()))) // CraftBukkit - Added this.checkMovement condition to solve this check being triggered by teleports + { + logger.warn(this.playerEntity.getCommandSenderName() + " moved too quickly! " + d4 + "," + d5 + "," + d6 + " (" + d7 + ", " + d8 + ", " + d9 + ")"); + this.setPlayerLocation(this.lastPosX, this.lastPosY, this.lastPosZ, this.playerEntity.rotationYaw, this.playerEntity.rotationPitch); +@@ -413,21 +607,85 @@ + + public void setPlayerLocation(double p_147364_1_, double p_147364_3_, double p_147364_5_, float p_147364_7_, float p_147364_8_) + { ++ // CraftBukkit start - Delegate to teleport(Location) ++ Player player = this.getPlayerB(); ++ Location from = player.getLocation(); ++ Location to = new Location(this.getPlayerB().getWorld(), p_147364_1_, p_147364_3_, p_147364_5_, p_147364_7_, p_147364_8_); ++ PlayerTeleportEvent event = new PlayerTeleportEvent(player, from, to, PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ this.server.getPluginManager().callEvent(event); ++ from = event.getFrom(); ++ to = event.isCancelled() ? from : event.getTo(); ++ this.teleport(to); ++ } ++ ++ public void teleport(Location dest) ++ { ++ double d0, d1, d2; ++ float f, f1; ++ d0 = dest.getX(); ++ d1 = dest.getY(); ++ d2 = dest.getZ(); ++ f = dest.getYaw(); ++ f1 = dest.getPitch(); ++ ++ // TODO: make sure this is the best way to address this. ++ if (Float.isNaN(f)) ++ { ++ f = 0; ++ } ++ ++ if (Float.isNaN(f1)) ++ { ++ f1 = 0; ++ } ++ ++ this.lastPosX = d0; ++ this.lastPosY = d1; ++ this.lastPosZ = d2; ++ this.lastYaw = f; ++ this.lastPitch = f1; ++ this.justTeleported = true; ++ // CraftBukkit end + this.hasMoved = false; +- this.lastPosX = p_147364_1_; +- this.lastPosY = p_147364_3_; +- this.lastPosZ = p_147364_5_; +- this.playerEntity.setPositionAndRotation(p_147364_1_, p_147364_3_, p_147364_5_, p_147364_7_, p_147364_8_); +- this.playerEntity.playerNetServerHandler.sendPacket(new S08PacketPlayerPosLook(p_147364_1_, p_147364_3_ + 1.6200000047683716D, p_147364_5_, p_147364_7_, p_147364_8_, false)); ++ this.lastPosX = d0; ++ this.lastPosY = d1; ++ this.lastPosZ = d2; ++ this.playerEntity.setPositionAndRotation(d0, d1, d2, f, f1); ++ this.playerEntity.playerNetServerHandler.sendPacket(new S08PacketPlayerPosLook(d0, d1 + 1.6200000047683716D, d2, f, f1, false)); + } + + public void processPlayerDigging(C07PacketPlayerDigging p_147345_1_) + { ++ if (this.playerEntity.isDead) ++ { ++ return; // CraftBukkit ++ } ++ + WorldServer worldserver = this.serverController.worldServerForDimension(this.playerEntity.dimension); + this.playerEntity.func_143004_u(); + + if (p_147345_1_.func_149506_g() == 4) + { ++ // CraftBukkit start ++ // If the ticks aren't the same then the count starts from 0 and we update the lastDropTick. ++ if (this.lastDropTick != MinecraftServer.currentTick) ++ { ++ this.dropCount = 0; ++ this.lastDropTick = MinecraftServer.currentTick; ++ } ++ else ++ { ++ // Else we increment the drop count and check the amount. ++ this.dropCount++; ++ ++ if (this.dropCount >= 20) ++ { ++ this.logger.warn(this.playerEntity.getCommandSenderName() + " dropped their items too quickly!"); ++ this.kickPlayerFromServer("You dropped your items too quickly (Hacking?)"); ++ return; ++ } ++ } ++ // CraftBukkit end + this.playerEntity.dropOneItem(false); + } + else if (p_147345_1_.func_149506_g() == 3) +@@ -490,7 +748,17 @@ + } + else + { ++ // CraftBukkit start ++ CraftEventFactory.callPlayerInteractEvent(this.playerEntity, Action.LEFT_CLICK_BLOCK, i, j, k, p_147345_1_.func_149501_f(), this.playerEntity.inventory.getCurrentItem()); + this.playerEntity.playerNetServerHandler.sendPacket(new S23PacketBlockChange(i, j, k, worldserver)); ++ // Update any tile entity data for this block ++ TileEntity tileentity = worldserver.getTileEntity(i, j, k); ++ ++ if (tileentity != null) ++ { ++ this.playerEntity.playerNetServerHandler.sendPacket(tileentity.getDescriptionPacket()); ++ } ++ // CraftBukkit end + } + } + else if (p_147345_1_.func_149506_g() == 2) +@@ -517,6 +785,39 @@ + public void processPlayerBlockPlacement(C08PacketPlayerBlockPlacement p_147346_1_) + { + WorldServer worldserver = this.serverController.worldServerForDimension(this.playerEntity.dimension); ++ // CraftBukkit start ++ if (this.playerEntity.isDead) ++ { ++ return; ++ } ++ ++ // This is a horrible hack needed because the client sends 2 packets on 'right mouse click' ++ // aimed at a block. We shouldn't need to get the second packet if the data is handled ++ // but we cannot know what the client will do, so we might still get it ++ // ++ // If the time between packets is small enough, and the 'signature' similar, we discard the ++ // second one. This sadly has to remain until Mojang makes their packets saner. :( ++ // -- Grum ++ if (p_147346_1_.func_149568_f() == 255) ++ { ++ if (p_147346_1_.func_149574_g() != null && p_147346_1_.func_149574_g().getItem() == this.lastMaterial && this.lastPacket != null ++ && p_147346_1_.timestamp - this.lastPacket < 100) ++ { ++ this.lastPacket = null; ++ return; ++ } ++ } ++ else ++ { ++ this.lastMaterial = p_147346_1_.func_149574_g() == null ? null : p_147346_1_.func_149574_g().getItem(); ++ this.lastPacket = p_147346_1_.timestamp; ++ } ++ ++ // CraftBukkit - if rightclick decremented the item, always send the update packet. */ ++ // this is not here for CraftBukkit's own functionality; rather it is to fix ++ // a notch bug where the item doesn't update correctly. ++ boolean always = false; ++ // CraftBukkit end + ItemStack itemstack = this.playerEntity.inventory.getCurrentItem(); + boolean flag = false; + boolean placeResult = true; +@@ -533,11 +834,21 @@ + return; + } + +- PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(playerEntity, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, 0, 0, 0, -1, worldserver); +- if (event.useItem != Event.Result.DENY) ++ PlayerInteractEvent forgeEvent = ForgeEventFactory.onPlayerInteract(playerEntity, PlayerInteractEvent.Action.RIGHT_CLICK_AIR, 0, 0, 0, -1, ++ worldserver); // Cauldron - rename event ++ // CraftBukkit start ++ int itemstackAmount = itemstack.stackSize; ++ org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.playerEntity, Action.RIGHT_CLICK_AIR, itemstack); ++ ++ if (forgeEvent.useItem != cpw.mods.fml.common.eventhandler.Event.Result.DENY && event.useItemInHand() != Event.Result.DENY) + { + this.playerEntity.theItemInWorldManager.tryUseItem(this.playerEntity, worldserver, itemstack); + } ++ // CraftBukkit - notch decrements the counter by 1 in the above method with food, ++ // snowballs and so forth, but he does it in a place that doesn't cause the ++ // inventory update packet to get sent ++ always = (itemstack.stackSize != itemstackAmount); ++ // CraftBukkit end + } + else if (p_147346_1_.func_149571_d() >= this.serverController.getBuildLimit() - 1 && (p_147346_1_.func_149568_f() == 1 || p_147346_1_.func_149571_d() >= this.serverController.getBuildLimit())) + { +@@ -548,17 +859,24 @@ + } + else + { +- double dist = playerEntity.theItemInWorldManager.getBlockReachDistance() + 1; +- dist *= dist; +- if (this.hasMoved && this.playerEntity.getDistanceSq((double)i + 0.5D, (double)j + 0.5D, (double)k + 0.5D) < dist && !this.serverController.isBlockProtected(worldserver, i, j, k, this.playerEntity)) ++ // CraftBukkit start - Check if we can actually do something over this large a distance ++ Location eyeLoc = this.getPlayerB().getEyeLocation(); ++ double reachDistance = NumberConversions.square(eyeLoc.getX() - i) + NumberConversions.square(eyeLoc.getY() - j) ++ + NumberConversions.square(eyeLoc.getZ() - k); ++ ++ if (reachDistance > (this.getPlayerB().getGameMode() == org.bukkit.GameMode.CREATIVE ? CREATIVE_PLACE_DISTANCE_SQUARED : SURVIVAL_PLACE_DISTANCE_SQUARED)) + { +- // record block place result so we can update client itemstack size if place event was cancelled. +- if (!this.playerEntity.theItemInWorldManager.activateBlockOrUseItem(this.playerEntity, worldserver, itemstack, i, j, k, l, p_147346_1_.func_149573_h(), p_147346_1_.func_149569_i(), p_147346_1_.func_149575_j())) +- { +- placeResult = false; +- } ++ return; + } + ++ // Cauldron start - record place result so we can update client inventory slot if place event is cancelled. Fixes stacksize client-side bug ++ if (!this.playerEntity.theItemInWorldManager.activateBlockOrUseItem(this.playerEntity, worldserver, itemstack, i, j, k, l, ++ p_147346_1_.func_149573_h(), p_147346_1_.func_149569_i(), p_147346_1_.func_149575_j())) ++ { ++ always = true; ++ } ++ // Cauldron end ++ // CraftBukkit end + flag = true; + } + +@@ -612,10 +930,17 @@ + this.playerEntity.isChangingQuantityOnly = true; + this.playerEntity.inventory.mainInventory[this.playerEntity.inventory.currentItem] = ItemStack.copyItemStack(this.playerEntity.inventory.mainInventory[this.playerEntity.inventory.currentItem]); + Slot slot = this.playerEntity.openContainer.getSlotFromInventory(this.playerEntity.inventory, this.playerEntity.inventory.currentItem); ++ // Cauldron start - abort if no slot, fixes RP2 timer crash block place - see #181 ++ if (slot == null) ++ { ++ this.playerEntity.isChangingQuantityOnly = false; // set flag to false or it will cause inventory to glitch on death ++ return; ++ } ++ // Cauldron end + this.playerEntity.openContainer.detectAndSendChanges(); + this.playerEntity.isChangingQuantityOnly = false; + +- if (!ItemStack.areItemStacksEqual(this.playerEntity.inventory.getCurrentItem(), p_147346_1_.func_149574_g()) || !placeResult) // force client itemstack update if place event was cancelled ++ if (!ItemStack.areItemStacksEqual(this.playerEntity.inventory.getCurrentItem(), p_147346_1_.func_149574_g()) || always) // Cauldron - always is needed to update client itemstack if placement is cancelled + { + this.sendPacket(new S2FPacketSetSlot(this.playerEntity.openContainer.windowId, slot.slotNumber, this.playerEntity.inventory.getCurrentItem())); + } +@@ -624,14 +949,34 @@ + + public void onDisconnect(IChatComponent p_147231_1_) + { +- logger.info(this.playerEntity.getCommandSenderName() + " lost connection: " + p_147231_1_); ++ // CraftBukkit start - Rarely it would send a disconnect line twice ++ if (this.processedDisconnect) ++ { ++ return; ++ } ++ else ++ { ++ this.processedDisconnect = true; ++ } ++ // CraftBukkit end ++ logger.info(this.playerEntity.getCommandSenderName() + " lost connection: " + p_147231_1_.getUnformattedText()); // CraftBukkit - Don't toString the component + this.serverController.func_147132_au(); +- ChatComponentTranslation chatcomponenttranslation = new ChatComponentTranslation("multiplayer.player.left", new Object[] {this.playerEntity.func_145748_c_()}); +- chatcomponenttranslation.getChatStyle().setColor(EnumChatFormatting.YELLOW); +- this.serverController.getConfigurationManager().sendChatMsg(chatcomponenttranslation); ++ // CraftBukkit start - Replace vanilla quit message handling with our own. ++ /* ++ ChatMessage chatcomponenttranslation = new ChatMessage("multiplayer.player.left", new Object[] { this.player.getScoreboardDisplayName()}); ++ ++ chatcomponenttranslation.b().setColor(EnumChatFormat.YELLOW); ++ this.minecraftServer.getPlayerList().sendMessage(chatcomponenttranslation); ++ */ + this.playerEntity.mountEntityAndWakeUp(); +- this.serverController.getConfigurationManager().playerLoggedOut(this.playerEntity); ++ String quitMessage = this.serverController.getConfigurationManager().disconnect(this.playerEntity); + ++ if ((quitMessage != null) && (quitMessage.length() > 0)) ++ { ++ this.serverController.getConfigurationManager().sendMessage(CraftChatMessage.fromString(quitMessage)); ++ } ++ // CraftBukkit end ++ + if (this.serverController.isSinglePlayer() && this.playerEntity.getCommandSenderName().equals(this.serverController.getServerOwner())) + { + logger.info("Stopping singleplayer server as player logged out"); +@@ -657,6 +1002,18 @@ + } + } + ++ // CraftBukkit start ++ if (p_147359_1_ == null) ++ { ++ return; ++ } ++ else if (p_147359_1_ instanceof S05PacketSpawnPosition) ++ { ++ S05PacketSpawnPosition packet6 = (S05PacketSpawnPosition) p_147359_1_; ++ this.playerEntity.compassTarget = new Location(this.getPlayerB().getWorld(), packet6.field_149364_a, packet6.field_149362_b, packet6.field_149363_c); ++ } ++ // CraftBukkit end ++ + try + { + this.netManager.scheduleOutboundPacket(p_147359_1_, new GenericFutureListener[0]); +@@ -679,20 +1036,37 @@ + + public void processHeldItemChange(C09PacketHeldItemChange p_147355_1_) + { ++ // CraftBukkit start ++ if (this.playerEntity.isDead) ++ { ++ return; ++ } ++ + if (p_147355_1_.func_149614_c() >= 0 && p_147355_1_.func_149614_c() < InventoryPlayer.getHotbarSize()) + { ++ PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getPlayerB(), this.playerEntity.inventory.currentItem, p_147355_1_.func_149614_c()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ this.sendPacket(new S09PacketHeldItemChange(this.playerEntity.inventory.currentItem)); ++ this.playerEntity.func_143004_u(); ++ return; ++ } ++ // CraftBukkit end + this.playerEntity.inventory.currentItem = p_147355_1_.func_149614_c(); + this.playerEntity.func_143004_u(); + } + else + { + logger.warn(this.playerEntity.getCommandSenderName() + " tried to set an invalid carried item"); ++ this.kickPlayerFromServer("Nope!"); // CraftBukkit + } + } + + public void processChatMessage(C01PacketChatMessage p_147354_1_) + { +- if (this.playerEntity.func_147096_v() == EntityPlayer.EnumChatVisibility.HIDDEN) ++ if (this.playerEntity.isDead || this.playerEntity.func_147096_v() == EntityPlayer.EnumChatVisibility.HIDDEN) // CraftBukkit - dead men tell no tales + { + ChatComponentTranslation chatcomponenttranslation = new ChatComponentTranslation("chat.cannotSend", new Object[0]); + chatcomponenttranslation.getChatStyle().setColor(EnumChatFormatting.RED); +@@ -708,51 +1082,385 @@ + { + if (!ChatAllowedCharacters.isAllowedCharacter(s.charAt(i))) + { +- this.kickPlayerFromServer("Illegal characters in chat"); ++ // CraftBukkit start - threadsafety ++ if (p_147354_1_.hasPriority()) ++ { ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() ++ { ++ NetHandlerPlayServer.this.kickPlayerFromServer("Illegal characters in chat"); ++ return null; ++ } ++ }; ++ this.serverController.processQueue.add(waitable); ++ ++ try ++ { ++ waitable.get(); ++ } ++ catch (InterruptedException e) ++ { ++ Thread.currentThread().interrupt(); ++ } ++ catch (ExecutionException e) ++ { ++ throw new RuntimeException(e); ++ } ++ } ++ else ++ { ++ this.kickPlayerFromServer("Illegal characters in chat"); ++ } ++ // CraftBukkit end + return; + } + } + +- if (s.startsWith("/")) ++ // CraftBukkit start ++ if (!p_147354_1_.hasPriority()) + { +- this.handleSlashCommand(s); ++ try ++ { ++ this.serverController.server.playerCommandState = true; ++ this.handleSlashCommand(s); ++ } ++ finally ++ { ++ this.serverController.server.playerCommandState = false; ++ } + } +- else ++ else if (s.isEmpty()) + { +- ChatComponentTranslation chatcomponenttranslation1 = new ChatComponentTranslation("chat.type.text", new Object[] {this.playerEntity.func_145748_c_(), ForgeHooks.newChatWithLinks(s)}); // Fixes chat links +- chatcomponenttranslation1 = ForgeHooks.onServerChatEvent(this, s, chatcomponenttranslation1); +- if (chatcomponenttranslation1 == null) return; +- this.serverController.getConfigurationManager().sendChatMsgImpl(chatcomponenttranslation1, false); ++ logger.warn(this.playerEntity.getCommandSenderName() + " tried to send an empty message"); + } ++ else if (getPlayerB().isConversing()) ++ { ++ getPlayerB().acceptConversationInput(s); ++ } ++ else if (this.playerEntity.func_147096_v() == EntityPlayer.EnumChatVisibility.SYSTEM) // Re-add "Command Only" flag check ++ { ++ ChatComponentTranslation chatcomponenttranslation = new ChatComponentTranslation("chat.cannotSend", new Object[0]); ++ chatcomponenttranslation.getChatStyle().setColor(EnumChatFormatting.RED); ++ this.sendPacket(new S02PacketChat(chatcomponenttranslation)); ++ } ++ else if (true) ++ { ++ this.chat(s, true); ++ // CraftBukkit end - the below is for reference. :) ++ } + +- this.chatSpamThresholdCount += 20; ++ // CraftBukkit start - replaced with thread safe throttle ++ // this.chatSpamThresholdCount += 20; ++ if (chatSpamField.addAndGet(this, 20) > 200 && !this.serverController.getConfigurationManager().func_152596_g(this.playerEntity.getGameProfile())) ++ { ++ if (p_147354_1_.hasPriority()) ++ { ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() ++ { ++ NetHandlerPlayServer.this.kickPlayerFromServer("disconnect.spam"); ++ return null; ++ } ++ }; ++ this.serverController.processQueue.add(waitable); + +- if (this.chatSpamThresholdCount > 200 && !this.serverController.getConfigurationManager().func_152596_g(this.playerEntity.getGameProfile())) ++ try ++ { ++ waitable.get(); ++ } ++ catch (InterruptedException e) ++ { ++ Thread.currentThread().interrupt(); ++ } ++ catch (ExecutionException e) ++ { ++ throw new RuntimeException(e); ++ } ++ } ++ else ++ { ++ this.kickPlayerFromServer("disconnect.spam"); ++ } ++ ++ // CraftBukkit end ++ } ++ } ++ } ++ ++ // CraftBukkit start ++ public void chat(String s, boolean async) ++ { ++ if (s.isEmpty() || this.playerEntity.func_147096_v() == EntityPlayer.EnumChatVisibility.HIDDEN) ++ { ++ return; ++ } ++ ++ if (!async && s.startsWith("/")) ++ { ++ this.handleSlashCommand(s); ++ } ++ else if (this.playerEntity.func_147096_v() == EntityPlayer.EnumChatVisibility.SYSTEM) ++ { ++ // Do nothing, this is coming from a plugin ++ } ++ else ++ { ++ // Cauldron start - handle Forge event ++ ChatComponentTranslation chatcomponenttranslation1 = new ChatComponentTranslation("chat.type.text", new Object[] { ++ this.playerEntity.func_145748_c_(), s }); ++ chatcomponenttranslation1 = ForgeHooks.onServerChatEvent(this, s, chatcomponenttranslation1); ++ ++ if (chatcomponenttranslation1 != null ++ && chatcomponenttranslation1.getFormatArgs()[chatcomponenttranslation1.getFormatArgs().length - 1] instanceof String) + { +- this.kickPlayerFromServer("disconnect.spam"); ++ // use event message from Forge ++ s = (String) chatcomponenttranslation1.getFormatArgs()[chatcomponenttranslation1.getFormatArgs().length - 1]; + } ++ // Cauldron end ++ Player player = this.getPlayerB(); ++ AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(async, player, s, new LazyPlayerSet()); // Cauldron - pass changed message if any from Forge ++ event.setCancelled(chatcomponenttranslation1 == null); // Cauldron - pre-cancel event if forge event was cancelled ++ this.server.getPluginManager().callEvent(event); ++ if (PlayerChatEvent.getHandlerList().getRegisteredListeners().length != 0) ++ { ++ // Evil plugins still listening to deprecated event ++ final PlayerChatEvent queueEvent = new PlayerChatEvent(player, event.getMessage(), event.getFormat(), event.getRecipients()); ++ queueEvent.setCancelled(event.isCancelled()); ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() ++ { ++ org.bukkit.Bukkit.getPluginManager().callEvent(queueEvent); ++ ++ if (queueEvent.isCancelled()) ++ { ++ return null; ++ } ++ ++ String message = String.format(queueEvent.getFormat(), queueEvent.getPlayer().getDisplayName(), queueEvent.getMessage()); ++ NetHandlerPlayServer.this.serverController.console.sendMessage(message); ++ if (((LazyPlayerSet) queueEvent.getRecipients()).isLazy()) ++ { ++ for (Object recipient : serverController.getConfigurationManager().playerEntityList) ++ { ++ ((EntityPlayerMP) recipient).sendMessage(CraftChatMessage.fromString(message)); ++ } ++ } ++ else ++ { ++ for (Player player : queueEvent.getRecipients()) ++ { ++ player.sendMessage(message); ++ } ++ } ++ ++ return null; ++ } ++ }; ++ ++ if (async) ++ { ++ serverController.processQueue.add(waitable); ++ } ++ else ++ { ++ waitable.run(); ++ } ++ ++ try ++ { ++ waitable.get(); ++ } ++ catch (InterruptedException e) ++ { ++ Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on! ++ } ++ catch (ExecutionException e) ++ { ++ throw new RuntimeException("Exception processing chat event", e.getCause()); ++ } ++ } ++ else ++ { ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ ++ s = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()); ++ serverController.console.sendMessage(s); ++ if (((LazyPlayerSet) event.getRecipients()).isLazy()) ++ { ++ for (Object recipient : serverController.getConfigurationManager().playerEntityList) ++ { ++ for (IChatComponent component : CraftChatMessage.fromString(s)) ++ { ++ ((EntityPlayerMP) recipient).sendMessage(CraftChatMessage.fromString(s)); ++ } ++ } ++ } ++ else ++ { ++ for (Player recipient : event.getRecipients()) ++ { ++ recipient.sendMessage(s); ++ } ++ } ++ } + } + } ++ // CraftBukkit end + + private void handleSlashCommand(String p_147361_1_) + { +- this.serverController.getCommandManager().executeCommand(this.playerEntity, p_147361_1_); ++ org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.startTiming(); // Spigot ++ // CraftBukkit start ++ CraftPlayer player = this.getPlayerB(); ++ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, p_147361_1_, new LazyPlayerSet()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.stopTiming(); // Spigot ++ return; ++ } ++ ++ try ++ { ++ // Spigot Start ++ if (org.spigotmc.SpigotConfig.logCommands) ++ { ++ this.logger.info(event.getPlayer().getName() + " issued server command: " + event.getMessage()); // CraftBukkit ++ } ++ ++ // Spigot end ++ // Cauldron start - handle bukkit/vanilla commands ++ int space = event.getMessage().indexOf(" "); ++ // if bukkit command exists then execute it over vanilla ++ if (this.server.getCommandMap().getCommand(event.getMessage().substring(1, space != -1 ? space : event.getMessage().length())) != null) ++ { ++ this.server.dispatchCommand(event.getPlayer(), event.getMessage().substring(1)); ++ org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.stopTiming(); // Spigot ++ return; ++ } ++ else ++ // process vanilla command ++ { ++ this.server.dispatchVanillaCommand(event.getPlayer(), event.getMessage().substring(1)); ++ org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.stopTiming(); // Spigot ++ return; ++ } ++ } ++ catch (org.bukkit.command.CommandException ex) ++ { ++ player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command"); ++ java.util.logging.Logger.getLogger(NetHandlerPlayServer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); ++ org.bukkit.craftbukkit.SpigotTimings.playerCommandTimer.stopTiming(); // Spigot ++ return; ++ } ++ ++ // this.serverController.getCommandManager().executeCommand(this.playerEntity, p_147361_1_); ++ // CraftBukkit end + } + + public void processAnimation(C0APacketAnimation p_147350_1_) + { ++ if (this.playerEntity.isDead) ++ { ++ return; // CraftBukkit ++ } ++ + this.playerEntity.func_143004_u(); + + if (p_147350_1_.func_149421_d() == 1) + { ++ // CraftBukkit start - Raytrace to look for 'rogue armswings' ++ float f = 1.0F; ++ float f1 = this.playerEntity.prevRotationPitch + (this.playerEntity.rotationPitch - this.playerEntity.prevRotationPitch) * f; ++ float f2 = this.playerEntity.prevRotationYaw + (this.playerEntity.rotationYaw - this.playerEntity.prevRotationYaw) * f; ++ double d0 = this.playerEntity.prevPosX + (this.playerEntity.posX - this.playerEntity.prevPosX) * (double) f; ++ double d1 = this.playerEntity.prevPosY + (this.playerEntity.posY - this.playerEntity.prevPosY) * (double) f + 1.62D ++ - (double) this.playerEntity.yOffset; ++ double d2 = this.playerEntity.prevPosZ + (this.playerEntity.posZ - this.playerEntity.prevPosZ) * (double) f; ++ Vec3 vec3 = Vec3.createVectorHelper(d0, d1, d2); ++ float f3 = MathHelper.cos(-f2 * 0.017453292F - (float) Math.PI); ++ float f4 = MathHelper.sin(-f2 * 0.017453292F - (float) Math.PI); ++ float f5 = -MathHelper.cos(-f1 * 0.017453292F); ++ float f6 = MathHelper.sin(-f1 * 0.017453292F); ++ float f7 = f4 * f5; ++ float f8 = f3 * f5; ++ double d3 = 5.0D; ++ Vec3 vec31 = vec3.addVector((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); ++ MovingObjectPosition movingobjectposition = this.playerEntity.worldObj.rayTraceBlocks(vec3, vec31, true); ++ boolean valid = false; ++ ++ if (movingobjectposition == null || movingobjectposition.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) ++ { ++ valid = true; ++ } ++ else ++ { ++ Block block = this.playerEntity.worldObj.getBlock(movingobjectposition.blockX, movingobjectposition.blockY, movingobjectposition.blockZ); ++ ++ if (!block.isOpaqueCube()) // Should be isBreakable? ++ { ++ valid = true; ++ } ++ } ++ ++ if (valid) ++ { ++ CraftEventFactory.callPlayerInteractEvent(this.playerEntity, Action.LEFT_CLICK_AIR, this.playerEntity.inventory.getCurrentItem()); ++ } ++ ++ // Arm swing animation ++ PlayerAnimationEvent event = new PlayerAnimationEvent(this.getPlayerB()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ // CraftBukkit end + this.playerEntity.swingItem(); + } + } + + public void processEntityAction(C0BPacketEntityAction p_147357_1_) + { ++ // CraftBukkit start ++ if (this.playerEntity.isDead) ++ { ++ return; ++ } ++ + this.playerEntity.func_143004_u(); + ++ if (p_147357_1_.func_149513_d() == 1 || p_147357_1_.func_149513_d() == 2) ++ { ++ PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this.getPlayerB(), p_147357_1_.func_149513_d() == 1); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ } ++ ++ if (p_147357_1_.func_149513_d() == 4 || p_147357_1_.func_149513_d() == 5) ++ { ++ PlayerToggleSprintEvent event = new PlayerToggleSprintEvent(this.getPlayerB(), p_147357_1_.func_149513_d() == 4); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ } ++ // CraftBukkit end ++ + if (p_147357_1_.func_149513_d() == 1) + { + this.playerEntity.setSneaking(true); +@@ -772,7 +1480,7 @@ + else if (p_147357_1_.func_149513_d() == 3) + { + this.playerEntity.wakeUpPlayer(false, true, true); +- this.hasMoved = false; ++ // this.hasMoved = false; // CraftBukkit - this is handled in teleport + } + else if (p_147357_1_.func_149513_d() == 6) + { +@@ -789,8 +1497,20 @@ + + public void processUseEntity(C02PacketUseEntity p_147340_1_) + { ++ if (this.playerEntity.isDead) ++ { ++ return; // CraftBukkit ++ } ++ + WorldServer worldserver = this.serverController.worldServerForDimension(this.playerEntity.dimension); +- Entity entity = p_147340_1_.func_149564_a(worldserver); ++ Entity entity = p_147340_1_.func_149564_a((World) worldserver); ++ // Spigot Start ++ if (entity == playerEntity) ++ { ++ kickPlayerFromServer("Cannot interact with self!"); ++ return; ++ } ++ // Spigot End + this.playerEntity.func_143004_u(); + + if (entity != null) +@@ -805,9 +1525,53 @@ + + if (this.playerEntity.getDistanceSqToEntity(entity) < d0) + { ++ ItemStack itemInHand = this.playerEntity.inventory.getCurrentItem(); // CraftBukkit ++ + if (p_147340_1_.func_149565_c() == C02PacketUseEntity.Action.INTERACT) + { ++ // CraftBukkit start ++ boolean triggerTagUpdate = itemInHand != null && itemInHand.getItem() == Items.name_tag && entity instanceof EntityLiving; ++ boolean triggerChestUpdate = itemInHand != null && itemInHand.getItem() == Item.getItemFromBlock(Blocks.chest) ++ && entity instanceof EntityHorse; ++ boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.lead && entity instanceof EntityLiving; ++ PlayerInteractEntityEvent event = new PlayerInteractEntityEvent((Player) this.getPlayerB(), entity.getBukkitEntity()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (triggerLeashUpdate ++ && (event.isCancelled() || this.playerEntity.inventory.getCurrentItem() == null || this.playerEntity.inventory.getCurrentItem() ++ .getItem() != Items.lead)) ++ { ++ // Refresh the current leash state ++ this.sendPacket(new S1BPacketEntityAttach(1, entity, ((EntityLiving) entity).getLeashedToEntity())); ++ } ++ ++ if (triggerTagUpdate ++ && (event.isCancelled() || this.playerEntity.inventory.getCurrentItem() == null || this.playerEntity.inventory.getCurrentItem() ++ .getItem() != Items.name_tag)) ++ { ++ // Refresh the current entity metadata ++ this.sendPacket(new S1CPacketEntityMetadata(entity.getEntityId(), entity.dataWatcher, true)); ++ } ++ ++ if (triggerChestUpdate ++ && (event.isCancelled() || this.playerEntity.inventory.getCurrentItem() == null || this.playerEntity.inventory.getCurrentItem() ++ .getItem() != Item.getItemFromBlock(Blocks.chest))) ++ { ++ this.sendPacket(new S1CPacketEntityMetadata(entity.getEntityId(), entity.dataWatcher, true)); ++ } ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ // CraftBukkit end + this.playerEntity.interactWith(entity); ++ // CraftBukkit start ++ if (itemInHand != null && itemInHand.stackSize <= -1) ++ { ++ this.playerEntity.sendContainerToPlayer(this.playerEntity.openContainer); ++ } ++ // CraftBukkit end + } + else if (p_147340_1_.func_149565_c() == C02PacketUseEntity.Action.ATTACK) + { +@@ -819,6 +1583,13 @@ + } + + this.playerEntity.attackTargetEntityWithCurrentItem(entity); ++ ++ // CraftBukkit start ++ if (itemInHand != null && itemInHand.stackSize <= -1) ++ { ++ this.playerEntity.sendContainerToPlayer(this.playerEntity.openContainer); ++ } ++ // CraftBukkit end + } + } + } +@@ -834,8 +1605,19 @@ + case 1: + if (this.playerEntity.playerConqueredTheEnd) + { +- this.playerEntity = this.serverController.getConfigurationManager().respawnPlayer(this.playerEntity, 0, true); ++ // Cauldron start ++ if (this.playerEntity.dimension == -1) // coming from end ++ { ++ // We really should be calling transferPlayerToDimension since the player is coming in contact with a portal. ++ this.serverController.getConfigurationManager().respawnPlayer(this.playerEntity, 0, true); // set flag to indicate player is leaving end. + } ++ else ++ // not coming from end ++ { ++ this.playerEntity = this.serverController.getConfigurationManager().respawnPlayer(this.playerEntity, 0, false); ++ } ++ // Cauldron end ++ } + else if (this.playerEntity.getServerForPlayer().getWorldInfo().isHardcoreModeEnabled()) + { + if (this.serverController.isSinglePlayer() && this.playerEntity.getCommandSenderName().equals(this.serverController.getServerOwner())) +@@ -871,17 +1653,461 @@ + + public void processCloseWindow(C0DPacketCloseWindow p_147356_1_) + { ++ if (this.playerEntity.isDead) ++ { ++ return; // CraftBukkit ++ } ++ ++ // Cauldron start - vanilla compatibility ++ try ++ { ++ if (this.playerEntity.openContainer.getBukkitView() != null) ++ { ++ CraftEventFactory.handleInventoryCloseEvent(this.playerEntity); // CraftBukkit ++ } ++ } ++ catch (AbstractMethodError e) ++ { ++ // do nothing ++ } ++ // Cauldron end + this.playerEntity.closeContainer(); + } + + public void processClickWindow(C0EPacketClickWindow p_147351_1_) + { ++ if (this.playerEntity.isDead) ++ { ++ return; // CraftBukkit ++ } ++ + this.playerEntity.func_143004_u(); + + if (this.playerEntity.openContainer.windowId == p_147351_1_.func_149548_c() && this.playerEntity.openContainer.isPlayerNotUsingContainer(this.playerEntity)) + { +- ItemStack itemstack = this.playerEntity.openContainer.slotClick(p_147351_1_.func_149544_d(), p_147351_1_.func_149543_e(), p_147351_1_.func_149542_h(), this.playerEntity); ++ // CraftBukkit start - Call InventoryClickEvent ++ if (p_147351_1_.func_149544_d() < -1 && p_147351_1_.func_149544_d() != -999) ++ { ++ return; ++ } + ++ InventoryView inventory = this.playerEntity.openContainer.getBukkitView(); ++ SlotType type = CraftInventoryView.getSlotType(inventory, p_147351_1_.func_149544_d()); ++ InventoryClickEvent event = null; ++ ClickType click = ClickType.UNKNOWN; ++ InventoryAction action = InventoryAction.UNKNOWN; ++ ItemStack itemstack = null; ++ ++ // Cauldron start - some containers such as NEI's Creative Container does not have a view at this point so we need to create one ++ if (inventory == null) ++ { ++ inventory = new CraftInventoryView(this.playerEntity.getBukkitEntity(), MinecraftServer.getServer().server.createInventory( ++ this.playerEntity.getBukkitEntity(), InventoryType.CHEST), this.playerEntity.openContainer); ++ this.playerEntity.openContainer.bukkitView = inventory; ++ } ++ // Cauldron end ++ ++ if (p_147351_1_.func_149544_d() == -1) ++ { ++ type = SlotType.OUTSIDE; // override ++ click = p_147351_1_.func_149543_e() == 0 ? ClickType.WINDOW_BORDER_LEFT : ClickType.WINDOW_BORDER_RIGHT; ++ action = InventoryAction.NOTHING; ++ } ++ else if (p_147351_1_.func_149542_h() == 0) ++ { ++ if (p_147351_1_.func_149543_e() == 0) ++ { ++ click = ClickType.LEFT; ++ } ++ else if (p_147351_1_.func_149543_e() == 1) ++ { ++ click = ClickType.RIGHT; ++ } ++ ++ if (p_147351_1_.func_149543_e() == 0 || p_147351_1_.func_149543_e() == 1) ++ { ++ action = InventoryAction.NOTHING; // Don't want to repeat ourselves ++ ++ if (p_147351_1_.func_149544_d() == -999) ++ { ++ if (playerEntity.inventory.getItemStack() != null) ++ { ++ action = p_147351_1_.func_149543_e() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR; ++ } ++ } ++ else ++ { ++ Slot slot = this.playerEntity.openContainer.getSlot(p_147351_1_.func_149544_d()); ++ ++ if (slot != null) ++ { ++ ItemStack clickedItem = slot.getStack(); ++ ItemStack cursor = playerEntity.inventory.getItemStack(); ++ ++ if (clickedItem == null) ++ { ++ if (cursor != null) ++ { ++ action = p_147351_1_.func_149543_e() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE; ++ } ++ } ++ else if (slot.canTakeStack(playerEntity)) // Should be Slot.isPlayerAllowed ++ { ++ if (cursor == null) ++ { ++ action = p_147351_1_.func_149543_e() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF; ++ } ++ else if (slot.isItemValid(cursor)) // Should be Slot.isItemAllowed ++ { ++ if (clickedItem.isItemEqual(cursor) && ItemStack.areItemStackTagsEqual(clickedItem, cursor)) ++ { ++ int toPlace = p_147351_1_.func_149543_e() == 0 ? cursor.stackSize : 1; ++ toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.stackSize); ++ toPlace = Math.min(toPlace, slot.inventory.getInventoryStackLimit() - clickedItem.stackSize); ++ ++ if (toPlace == 1) ++ { ++ action = InventoryAction.PLACE_ONE; ++ } ++ else if (toPlace == cursor.stackSize) ++ { ++ action = InventoryAction.PLACE_ALL; ++ } ++ else if (toPlace < 0) ++ { ++ action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks ++ } ++ else if (toPlace != 0) ++ { ++ action = InventoryAction.PLACE_SOME; ++ } ++ } ++ else if (cursor.stackSize <= slot.getSlotStackLimit()) // Should be Slot.getMaxStackSize() ++ { ++ action = InventoryAction.SWAP_WITH_CURSOR; ++ } ++ } ++ else if (cursor.getItem() == clickedItem.getItem() ++ && (!cursor.getHasSubtypes() || cursor.getItemDamage() == clickedItem.getItemDamage()) ++ && ItemStack.areItemStackTagsEqual(cursor, clickedItem)) ++ { ++ if (clickedItem.stackSize >= 0) ++ { ++ if (clickedItem.stackSize + cursor.stackSize <= cursor.getMaxStackSize()) ++ { ++ // As of 1.5, this is result slots only ++ action = InventoryAction.PICKUP_ALL; ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ else if (p_147351_1_.func_149542_h() == 1) ++ { ++ if (p_147351_1_.func_149543_e() == 0) ++ { ++ click = ClickType.SHIFT_LEFT; ++ } ++ else if (p_147351_1_.func_149543_e() == 1) ++ { ++ click = ClickType.SHIFT_RIGHT; ++ } ++ ++ if (p_147351_1_.func_149543_e() == 0 || p_147351_1_.func_149543_e() == 1) ++ { ++ if (p_147351_1_.func_149544_d() < 0) ++ { ++ action = InventoryAction.NOTHING; ++ } ++ else ++ { ++ Slot slot = this.playerEntity.openContainer.getSlot(p_147351_1_.func_149544_d()); ++ ++ if (slot != null && slot.canTakeStack(this.playerEntity) && slot.getHasStack()) // Should be Slot.hasItem() ++ { ++ action = InventoryAction.MOVE_TO_OTHER_INVENTORY; ++ } ++ else ++ { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } ++ } ++ else if (p_147351_1_.func_149542_h() == 2) ++ { ++ if (p_147351_1_.func_149543_e() >= 0 && p_147351_1_.func_149543_e() < 9) ++ { ++ click = ClickType.NUMBER_KEY; ++ Slot clickedSlot = this.playerEntity.openContainer.getSlot(p_147351_1_.func_149544_d()); ++ ++ if (clickedSlot.canTakeStack(playerEntity)) ++ { ++ ItemStack hotbar = this.playerEntity.inventory.getStackInSlot(p_147351_1_.func_149543_e()); ++ boolean canCleanSwap = hotbar == null || (clickedSlot.inventory == playerEntity.inventory && clickedSlot.isItemValid(hotbar)); // the slot will accept the hotbar item ++ ++ if (clickedSlot.getHasStack()) ++ { ++ if (canCleanSwap) ++ { ++ action = InventoryAction.HOTBAR_SWAP; ++ } ++ else ++ { ++ int firstEmptySlot = playerEntity.inventory.getFirstEmptyStack(); // Should be Inventory.firstEmpty() ++ ++ if (firstEmptySlot > -1) ++ { ++ action = InventoryAction.HOTBAR_MOVE_AND_READD; ++ } ++ else ++ { ++ action = InventoryAction.NOTHING; // This is not sane! Mojang: You should test for other slots of same type ++ } ++ } ++ } ++ else if (!clickedSlot.getHasStack() && hotbar != null && clickedSlot.isItemValid(hotbar)) ++ { ++ action = InventoryAction.HOTBAR_SWAP; ++ } ++ else ++ { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ else ++ { ++ action = InventoryAction.NOTHING; ++ } ++ ++ // Special constructor for number key ++ event = new InventoryClickEvent(inventory, type, p_147351_1_.func_149544_d(), click, action, p_147351_1_.func_149543_e()); ++ } ++ } ++ else if (p_147351_1_.func_149542_h() == 3) ++ { ++ if (p_147351_1_.func_149543_e() == 2) ++ { ++ click = ClickType.MIDDLE; ++ ++ if (p_147351_1_.func_149544_d() == -999) ++ { ++ action = InventoryAction.NOTHING; ++ } ++ else ++ { ++ Slot slot = this.playerEntity.openContainer.getSlot(p_147351_1_.func_149544_d()); ++ ++ if (slot != null && slot.getHasStack() && playerEntity.capabilities.isCreativeMode && playerEntity.inventory.getItemStack() == null) ++ { ++ action = InventoryAction.CLONE_STACK; ++ } ++ else ++ { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } ++ else ++ { ++ click = ClickType.UNKNOWN; ++ action = InventoryAction.UNKNOWN; ++ } ++ } ++ else if (p_147351_1_.func_149542_h() == 4) ++ { ++ if (p_147351_1_.func_149544_d() >= 0) ++ { ++ if (p_147351_1_.func_149543_e() == 0) ++ { ++ click = ClickType.DROP; ++ Slot slot = this.playerEntity.openContainer.getSlot(p_147351_1_.func_149544_d()); ++ ++ if (slot != null && slot.getHasStack() && slot.canTakeStack(playerEntity) && slot.getStack() != null ++ && slot.getStack().getItem() != Item.getItemFromBlock(Blocks.air)) ++ { ++ action = InventoryAction.DROP_ONE_SLOT; ++ } ++ else ++ { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ else if (p_147351_1_.func_149543_e() == 1) ++ { ++ click = ClickType.CONTROL_DROP; ++ Slot slot = this.playerEntity.openContainer.getSlot(p_147351_1_.func_149544_d()); ++ ++ if (slot != null && slot.getHasStack() && slot.canTakeStack(playerEntity) && slot.getStack() != null ++ && slot.getStack().getItem() != Item.getItemFromBlock(Blocks.air)) ++ { ++ action = InventoryAction.DROP_ALL_SLOT; ++ } ++ else ++ { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } ++ else ++ { ++ // Sane default (because this happens when they are holding nothing. Don't ask why.) ++ click = ClickType.LEFT; ++ ++ if (p_147351_1_.func_149543_e() == 1) ++ { ++ click = ClickType.RIGHT; ++ } ++ ++ action = InventoryAction.NOTHING; ++ } ++ } ++ else if (p_147351_1_.func_149542_h() == 5) ++ { ++ itemstack = this.playerEntity.openContainer.slotClick(p_147351_1_.func_149544_d(), p_147351_1_.func_149543_e(), 5, this.playerEntity); ++ } ++ else if (p_147351_1_.func_149542_h() == 6) ++ { ++ click = ClickType.DOUBLE_CLICK; ++ action = InventoryAction.NOTHING; ++ ++ if (p_147351_1_.func_149544_d() >= 0 && this.playerEntity.inventory.getItemStack() != null) ++ { ++ ItemStack cursor = this.playerEntity.inventory.getItemStack(); ++ action = InventoryAction.NOTHING; ++ ++ // Quick check for if we have any of the item ++ // Cauldron start - can't call getContents() on modded IInventory; CB-added method ++ try ++ { ++ if (inventory.getTopInventory().contains(org.bukkit.Material.getMaterial(Item.getIdFromItem(cursor.getItem()))) ++ || inventory.getBottomInventory().contains(org.bukkit.Material.getMaterial(Item.getIdFromItem(cursor.getItem())))) ++ { ++ action = InventoryAction.COLLECT_TO_CURSOR; ++ } ++ } ++ catch (AbstractMethodError ex) ++ { ++ // nothing we can do ++ } ++ // Cauldron end ++ } ++ } ++ ++ // TODO check on updates ++ ++ if (p_147351_1_.func_149542_h() != 5) ++ { ++ if (click == ClickType.NUMBER_KEY) ++ { ++ event = new InventoryClickEvent(inventory, type, p_147351_1_.func_149544_d(), click, action, p_147351_1_.func_149543_e()); ++ } ++ else ++ { ++ event = new InventoryClickEvent(inventory, type, p_147351_1_.func_149544_d(), click, action); ++ } ++ ++ org.bukkit.inventory.Inventory top = inventory.getTopInventory(); ++ ++ if (p_147351_1_.func_149544_d() == 0 && top instanceof CraftingInventory) ++ { ++ // Cauldron start - vanilla compatibility (mod recipes) ++ org.bukkit.inventory.Recipe recipe = null; ++ try ++ { ++ recipe = ((CraftingInventory) top).getRecipe(); ++ } ++ catch (AbstractMethodError e) ++ { ++ // do nothing ++ } ++ // Cauldron end ++ ++ if (recipe != null) ++ { ++ if (click == ClickType.NUMBER_KEY) ++ { ++ event = new CraftItemEvent(recipe, inventory, type, p_147351_1_.func_149544_d(), click, action, p_147351_1_.func_149543_e()); ++ } ++ else ++ { ++ event = new CraftItemEvent(recipe, inventory, type, p_147351_1_.func_149544_d(), click, action); ++ } ++ } ++ } ++ ++ server.getPluginManager().callEvent(event); ++ ++ switch (event.getResult()) ++ { ++ case ALLOW: ++ case DEFAULT: ++ itemstack = this.playerEntity.openContainer.slotClick(p_147351_1_.func_149544_d(), p_147351_1_.func_149543_e(), ++ p_147351_1_.func_149542_h(), this.playerEntity); ++ break; ++ case DENY: ++ /* Needs enum constructor in InventoryAction ++ if (action.modifiesOtherSlots()) { ++ } else { ++ if (action.modifiesCursor()) { ++ this.player.playerConnection.sendPacket(new Packet103SetSlot(-1, -1, this.player.inventory.getCarried())); ++ } ++ if (action.modifiesClicked()) { ++ this.player.playerConnection.sendPacket(new Packet103SetSlot(this.player.activeContainer.windowId, packet102windowclick.slot, this.player.activeContainer.getSlot(packet102windowclick.slot).getItem())); ++ } ++ }*/ ++ switch (action) ++ { ++ // Modified other slots ++ case PICKUP_ALL: ++ case MOVE_TO_OTHER_INVENTORY: ++ case HOTBAR_MOVE_AND_READD: ++ case HOTBAR_SWAP: ++ case COLLECT_TO_CURSOR: ++ case UNKNOWN: ++ this.playerEntity.sendContainerToPlayer(this.playerEntity.openContainer); ++ break; ++ ++ // Modified cursor and clicked ++ case PICKUP_SOME: ++ case PICKUP_HALF: ++ case PICKUP_ONE: ++ case PLACE_ALL: ++ case PLACE_SOME: ++ case PLACE_ONE: ++ case SWAP_WITH_CURSOR: ++ this.playerEntity.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(-1, -1, this.playerEntity.inventory.getItemStack())); ++ this.playerEntity.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(this.playerEntity.openContainer.windowId, p_147351_1_ ++ .func_149544_d(), this.playerEntity.openContainer.getSlot(p_147351_1_.func_149544_d()).getStack())); ++ break; ++ ++ // Modified clicked only ++ case DROP_ALL_SLOT: ++ case DROP_ONE_SLOT: ++ this.playerEntity.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(this.playerEntity.openContainer.windowId, p_147351_1_ ++ .func_149544_d(), this.playerEntity.openContainer.getSlot(p_147351_1_.func_149544_d()).getStack())); ++ break; ++ ++ // Modified cursor only ++ case DROP_ALL_CURSOR: ++ case DROP_ONE_CURSOR: ++ case CLONE_STACK: ++ this.playerEntity.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(-1, -1, this.playerEntity.inventory.getItemStack())); ++ break; ++ ++ // Nothing ++ case NOTHING: ++ break; ++ } ++ ++ return; ++ } ++ } ++ // CraftBukkit end ++ + if (ItemStack.areItemStacksEqual(p_147351_1_.func_149546_g(), itemstack)) + { + this.playerEntity.playerNetServerHandler.sendPacket(new S32PacketConfirmTransaction(p_147351_1_.func_149548_c(), p_147351_1_.func_149547_f(), true)); +@@ -903,6 +2129,12 @@ + } + + this.playerEntity.sendContainerAndContentsToPlayer(this.playerEntity.openContainer, arraylist); ++ // CraftBukkit start - Send a Set Slot to update the crafting result slot ++ if (type == SlotType.RESULT && itemstack != null) ++ { ++ this.playerEntity.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(this.playerEntity.openContainer.windowId, 0, itemstack)); ++ } ++ // CraftBukkit end + } + } + } +@@ -925,9 +2157,61 @@ + boolean flag = p_147344_1_.func_149627_c() < 0; + ItemStack itemstack = p_147344_1_.func_149625_d(); + boolean flag1 = p_147344_1_.func_149627_c() >= 1 && p_147344_1_.func_149627_c() < 36 + InventoryPlayer.getHotbarSize(); +- boolean flag2 = itemstack == null || itemstack.getItem() != null; ++ // CraftBukkit - Add invalidItems check ++ boolean flag2 = itemstack == null || itemstack.getItem() != null && !invalidItems.contains(Item.getIdFromItem(itemstack.getItem())); + boolean flag3 = itemstack == null || itemstack.getItemDamage() >= 0 && itemstack.stackSize <= 64 && itemstack.stackSize > 0; ++ // CraftBukkit start - Call click event ++ if (flag ++ || (flag1 && !ItemStack.areItemStacksEqual(this.playerEntity.inventoryContainer.getSlot(p_147344_1_.func_149627_c()).getStack(), ++ p_147344_1_.func_149625_d()))) // Insist on valid slot ++ { ++ org.bukkit.entity.HumanEntity player = this.playerEntity.getBukkitEntity(); ++ InventoryView inventory = new CraftInventoryView(player, player.getInventory(), this.playerEntity.inventoryContainer); ++ org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(p_147344_1_.func_149625_d()); // Should be packet107setcreativeslot.newitem ++ SlotType type = SlotType.QUICKBAR; + ++ if (flag) ++ { ++ type = SlotType.OUTSIDE; ++ } ++ else if (p_147344_1_.func_149627_c() < 36) ++ { ++ if (p_147344_1_.func_149627_c() >= 5 && p_147344_1_.func_149627_c() < 9) ++ { ++ type = SlotType.ARMOR; ++ } ++ else ++ { ++ type = SlotType.CONTAINER; ++ } ++ } ++ ++ InventoryCreativeEvent event = new InventoryCreativeEvent(inventory, type, flag ? -999 : p_147344_1_.func_149627_c(), item); ++ server.getPluginManager().callEvent(event); ++ itemstack = CraftItemStack.asNMSCopy(event.getCursor()); ++ ++ switch (event.getResult()) ++ { ++ case ALLOW: ++ // Plugin cleared the id / stacksize checks ++ flag2 = flag3 = true; ++ break; ++ case DEFAULT: ++ break; ++ case DENY: ++ // Reset the slot ++ if (p_147344_1_.func_149627_c() >= 0) ++ { ++ this.playerEntity.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(this.playerEntity.inventoryContainer.windowId, p_147344_1_ ++ .func_149627_c(), this.playerEntity.inventoryContainer.getSlot(p_147344_1_.func_149627_c()).getStack())); ++ this.playerEntity.playerNetServerHandler.sendPacket(new S2FPacketSetSlot(-1, -1, null)); ++ } ++ ++ return; ++ } ++ } ++ // CraftBukkit end ++ + if (flag1 && flag2 && flag3) + { + if (itemstack == null) +@@ -956,6 +2240,11 @@ + + public void processConfirmTransaction(C0FPacketConfirmTransaction p_147339_1_) + { ++ if (this.playerEntity.isDead) ++ { ++ return; // CraftBukkit ++ } ++ + Short oshort = (Short)this.field_147372_n.lookup(this.playerEntity.openContainer.windowId); + + if (oshort != null && p_147339_1_.func_149533_d() == oshort.shortValue() && this.playerEntity.openContainer.windowId == p_147339_1_.func_149532_c() && !this.playerEntity.openContainer.isPlayerNotUsingContainer(this.playerEntity)) +@@ -966,6 +2255,11 @@ + + public void processUpdateSign(C12PacketUpdateSign p_147343_1_) + { ++ if (this.playerEntity.isDead) ++ { ++ return; // CraftBukkit ++ } ++ + this.playerEntity.func_143004_u(); + WorldServer worldserver = this.serverController.worldServerForDimension(this.playerEntity.dimension); + +@@ -980,6 +2274,7 @@ + if (!tileentitysign.func_145914_a() || tileentitysign.func_145911_b() != this.playerEntity) + { + this.serverController.logWarning("Player " + this.playerEntity.getCommandSenderName() + " just tried to change non-editable sign"); ++ this.sendPacket(new S33PacketUpdateSign(p_147343_1_.func_149588_c(), p_147343_1_.func_149586_d(), p_147343_1_.func_149585_e(), tileentitysign.signText)); // CraftBukkit + return; + } + } +@@ -990,6 +2285,7 @@ + for (j = 0; j < 4; ++j) + { + boolean flag = true; ++ p_147343_1_.func_149589_f()[j] = p_147343_1_.func_149589_f()[j].replaceAll("\uF700", "").replaceAll("\uF701", ""); // Spigot - Mac OSX sends weird chars + + if (p_147343_1_.func_149589_f()[j].length() > 15) + { +@@ -1018,7 +2314,29 @@ + int k = p_147343_1_.func_149586_d(); + i = p_147343_1_.func_149585_e(); + TileEntitySign tileentitysign1 = (TileEntitySign)tileentity; +- System.arraycopy(p_147343_1_.func_149589_f(), 0, tileentitysign1.signText, 0, 4); ++ // CraftBukkit start ++ Player player = this.server.getPlayer(this.playerEntity); ++ SignChangeEvent event = new SignChangeEvent((org.bukkit.craftbukkit.block.CraftBlock) player.getWorld().getBlockAt(j, k, i), ++ this.server.getPlayer(this.playerEntity), p_147343_1_.func_149589_f()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ for (int l = 0; l < 4; ++l) ++ { ++ tileentitysign1.signText[l] = event.getLine(l); ++ ++ if (tileentitysign1.signText[l] == null) ++ { ++ tileentitysign1.signText[l] = ""; ++ } ++ } ++ ++ tileentitysign1.field_145916_j = false; ++ } ++ ++ // System.arraycopy(p_147343_1_.func_149589_f(), 0, tileentitysign1.signText, 0, 4); ++ // CraftBukkit end + tileentitysign1.markDirty(); + worldserver.markBlockForUpdate(j, k, i); + } +@@ -1041,7 +2359,22 @@ + + public void processPlayerAbilities(C13PacketPlayerAbilities p_147348_1_) + { +- this.playerEntity.capabilities.isFlying = p_147348_1_.func_149488_d() && this.playerEntity.capabilities.allowFlying; ++ // CraftBukkit start - d() should be isFlying() ++ if (this.playerEntity.capabilities.allowFlying && this.playerEntity.capabilities.isFlying != p_147348_1_.func_149488_d()) ++ { ++ PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.server.getPlayer(this.playerEntity), p_147348_1_.func_149488_d()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ this.playerEntity.capabilities.isFlying = p_147348_1_.func_149488_d(); // Actually set the player's flying status ++ } ++ else ++ { ++ this.playerEntity.sendPlayerAbilities(); // Tell the player their ability was reverted ++ } ++ } ++ // CraftBukkit end + } + + public void processTabComplete(C14PacketTabComplete p_147341_1_) +@@ -1068,7 +2401,12 @@ + PacketBuffer packetbuffer; + ItemStack itemstack; + ItemStack itemstack1; +- ++ // CraftBukkit start - Ignore empty payloads ++ if (p_147349_1_.field_149560_b <= 0) ++ { ++ return; ++ } ++ // CraftBukkit end + if ("MC|BEdit".equals(p_147349_1_.func_149559_c())) + { + packetbuffer = new PacketBuffer(Unpooled.wrappedBuffer(p_147349_1_.func_149558_e())); +@@ -1093,16 +2431,18 @@ + + if (itemstack.getItem() == Items.writable_book && itemstack.getItem() == itemstack1.getItem()) + { +- itemstack1.setTagInfo("pages", itemstack.getTagCompound().getTagList("pages", 8)); ++ CraftEventFactory.handleEditBookEvent(playerEntity, itemstack); // CraftBukkit + } + + return; + } + } +- catch (Exception exception4) ++ // CraftBukkit start ++ catch (Throwable throwable) + { +- logger.error("Couldn\'t handle book info", exception4); +- return; ++ logger.error("Couldn\'t handle book info", throwable); ++ this.kickPlayerFromServer("Invalid book data!"); ++ // CraftBukkit end + } + finally + { +@@ -1135,19 +2475,18 @@ + { + if (itemstack.getItem() == Items.written_book && itemstack1.getItem() == Items.writable_book) + { +- itemstack1.setTagInfo("author", new NBTTagString(this.playerEntity.getCommandSenderName())); +- itemstack1.setTagInfo("title", new NBTTagString(itemstack.getTagCompound().getString("title"))); +- itemstack1.setTagInfo("pages", itemstack.getTagCompound().getTagList("pages", 8)); +- itemstack1.func_150996_a(Items.written_book); ++ CraftEventFactory.handleEditBookEvent(playerEntity, itemstack); // CraftBukkit + } + + return; + } + } +- catch (Exception exception3) ++ // CraftBukkit start ++ catch (Throwable exception1) + { +- logger.error("Couldn\'t sign book", exception3); +- return; ++ logger.error("Couldn\'t sign book", exception1); ++ this.kickPlayerFromServer("Invalid book data!"); ++ // CraftBukkit end + } + finally + { +@@ -1174,9 +2513,12 @@ + ((ContainerMerchant)container).setCurrentRecipeIndex(i); + } + } +- catch (Exception exception2) ++ // CraftBukkit start ++ catch (Throwable exception2) + { + logger.error("Couldn\'t select trade", exception2); ++ this.kickPlayerFromServer("Invalid trade data!"); ++ // CraftBukkit end + } + } + else if ("MC|AdvCdm".equals(p_147349_1_.func_149559_c())) +@@ -1222,9 +2564,12 @@ + this.playerEntity.addChatMessage(new ChatComponentTranslation("advMode.setCommand.success", new Object[] {s1})); + } + } +- catch (Exception exception1) ++ // CraftBukkit start ++ catch (Throwable exception3) + { +- logger.error("Couldn\'t set command block", exception1); ++ logger.error("Couldn\'t set command block", exception3); ++ this.kickPlayerFromServer("Invalid CommandBlock data!"); ++ // CraftBukkit end + } + finally + { +@@ -1257,9 +2602,12 @@ + tileentitybeacon.markDirty(); + } + } +- catch (Exception exception) ++ // CraftBukkit start ++ catch (Throwable exception4) + { +- logger.error("Couldn\'t set beacon", exception); ++ logger.error("Couldn\'t set beacon", exception4); ++ this.kickPlayerFromServer("Invalid beacon data!"); ++ // CraftBukkit end + } + } + } +@@ -1281,6 +2629,13 @@ + containerrepair.updateItemName(""); + } + } ++ // CraftBukkit start ++ // Cauldron - bukkit registration moved to FML's ChannelRegistrationHandler ++ else ++ { ++ server.getMessenger().dispatchIncomingMessage(playerEntity.getBukkitEntity(), p_147349_1_.func_149559_c(), p_147349_1_.func_149558_e()); ++ } ++ // CraftBukkit end + } + } + +@@ -1292,6 +2647,21 @@ + } + } + ++ // CraftBukkit start - Add "isDisconnected" method ++ public final boolean isDisconnected() ++ { ++ return !this.netManager.channel().config().isAutoRead(); ++ } ++ ++ // CraftBukkit end ++ ++ // Cauldron start ++ public CraftServer getCraftServer() ++ { ++ return this.server; ++ } ++ // Cauldron end ++ + static final class SwitchEnumState + { + static final int[] field_151290_a = new int[C16PacketClientStatus.EnumState.values().length]; diff --git a/patches/net/minecraft/network/NetworkManager.java.patch b/patches/net/minecraft/network/NetworkManager.java.patch new file mode 100644 index 0000000..da3f586 --- /dev/null +++ b/patches/net/minecraft/network/NetworkManager.java.patch @@ -0,0 +1,102 @@ +--- ../src-base/minecraft/net/minecraft/network/NetworkManager.java ++++ ../src-work/minecraft/net/minecraft/network/NetworkManager.java +@@ -25,6 +25,8 @@ + import java.net.InetAddress; + import java.net.SocketAddress; + import java.util.Queue; ++import java.util.UUID; ++ + import javax.crypto.SecretKey; + import net.minecraft.util.ChatComponentTranslation; + import net.minecraft.util.CryptManager; +@@ -38,6 +40,8 @@ + import org.apache.logging.log4j.Logger; + import org.apache.logging.log4j.Marker; + import org.apache.logging.log4j.MarkerManager; ++import com.mojang.authlib.properties.Property; ++import com.google.common.collect.ImmutableSet; // Spigot + + public class NetworkManager extends SimpleChannelInboundHandler + { +@@ -54,13 +58,32 @@ + private final Queue receivedPacketsQueue = Queues.newConcurrentLinkedQueue(); + private final Queue outboundPacketsQueue = Queues.newConcurrentLinkedQueue(); + private Channel channel; +- private SocketAddress socketAddress; ++ // Spigot start ++ public SocketAddress socketAddress; ++ public Property[] spoofedProfile; ++ public UUID spoofedUUID; ++ // Spigot end + private INetHandler netHandler; + private EnumConnectionState connectionState; + private IChatComponent terminationReason; + private boolean field_152463_r; + private static final String __OBFID = "CL_00001240"; + ++ // Spigot Start ++ public static final AttributeKey protocolVersion = new AttributeKey("protocol_version"); ++ public static final ImmutableSet SUPPORTED_VERSIONS = ImmutableSet.of(4, 5); ++ public static final int CURRENT_VERSION = 5; ++ public static int getVersion(Channel attr) ++ { ++ Integer ver = attr.attr( protocolVersion ).get(); ++ return ( ver != null ) ? ver : CURRENT_VERSION; ++ } ++ public int getVersion() ++ { ++ return getVersion( this.channel ); ++ } ++ // Spigot End ++ + public NetworkManager(boolean p_i45147_1_) + { + this.isClientSide = p_i45147_1_; +@@ -71,6 +94,7 @@ + super.channelActive(p_channelActive_1_); + this.channel = p_channelActive_1_.channel(); + this.socketAddress = this.channel.remoteAddress(); ++ this.field_152463_r = false; // Spigot + this.setConnectionState(EnumConnectionState.HANDSHAKING); + } + +@@ -208,7 +232,15 @@ + { + for (int i = 1000; !this.receivedPacketsQueue.isEmpty() && i >= 0; --i) + { +- Packet packet = (Packet)this.receivedPacketsQueue.poll(); ++ Packet packet = (Packet) this.receivedPacketsQueue.poll(); ++ ++ // CraftBukkit start ++ if (!this.isChannelOpen() || !this.channel.config().isAutoRead()) // Should be isConnected ++ { ++ continue; ++ } ++ ++ // CraftBukkit end + packet.processPacket(this.netHandler); + } + +@@ -225,6 +257,8 @@ + + public void closeChannel(IChatComponent p_150718_1_) + { ++ this.field_152463_r = false; // Spigot ++ + if (this.channel.isOpen()) + { + this.channel.close(); +@@ -322,6 +356,13 @@ + return channel; + } + ++ // Spigot Start ++ public SocketAddress getRawAddress() ++ { ++ return this.channel.remoteAddress(); ++ } ++ // Spigot End ++ + static class InboundHandlerTuplePacketListener + { + private final Packet field_150774_a; diff --git a/patches/net/minecraft/network/NetworkSystem.java.patch b/patches/net/minecraft/network/NetworkSystem.java.patch new file mode 100644 index 0000000..e799ed3 --- /dev/null +++ b/patches/net/minecraft/network/NetworkSystem.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/net/minecraft/network/NetworkSystem.java ++++ ../src-work/minecraft/net/minecraft/network/NetworkSystem.java +@@ -139,6 +139,14 @@ + + synchronized (this.networkManagers) + { ++ // Spigot Start ++ // This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order ++ if (org.spigotmc.SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % org.spigotmc.SpigotConfig.playerShuffle == 0) ++ { ++ Collections.shuffle(this.networkManagers); ++ } ++ ++ // Spigot End + Iterator iterator = this.networkManagers.iterator(); + + while (iterator.hasNext()) diff --git a/patches/net/minecraft/network/Packet.java.patch b/patches/net/minecraft/network/Packet.java.patch new file mode 100644 index 0000000..6ff6cbb --- /dev/null +++ b/patches/net/minecraft/network/Packet.java.patch @@ -0,0 +1,10 @@ +--- ../src-base/minecraft/net/minecraft/network/Packet.java ++++ ../src-work/minecraft/net/minecraft/network/Packet.java +@@ -9,6 +9,7 @@ + public abstract class Packet + { + private static final Logger logger = LogManager.getLogger(); ++ public final long timestamp = System.currentTimeMillis(); // CraftBukkit + private static final String __OBFID = "CL_00001272"; + + public static Packet generatePacket(BiMap p_148839_0_, int p_148839_1_) diff --git a/patches/net/minecraft/network/PacketBuffer.java.patch b/patches/net/minecraft/network/PacketBuffer.java.patch new file mode 100644 index 0000000..5dcea93 --- /dev/null +++ b/patches/net/minecraft/network/PacketBuffer.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/network/PacketBuffer.java ++++ ../src-work/minecraft/net/minecraft/network/PacketBuffer.java +@@ -98,7 +98,7 @@ + + public void writeItemStackToBuffer(ItemStack p_150788_1_) throws IOException + { +- if (p_150788_1_ == null) ++ if (p_150788_1_ == null || p_150788_1_.getItem() == null) // CraftBukkit - NPE fix itemstack.getItem() + { + this.writeShort(-1); + } diff --git a/patches/net/minecraft/network/handshake/client/C00Handshake.java.patch b/patches/net/minecraft/network/handshake/client/C00Handshake.java.patch new file mode 100644 index 0000000..13d9eb5 --- /dev/null +++ b/patches/net/minecraft/network/handshake/client/C00Handshake.java.patch @@ -0,0 +1,22 @@ +--- ../src-base/minecraft/net/minecraft/network/handshake/client/C00Handshake.java ++++ ../src-work/minecraft/net/minecraft/network/handshake/client/C00Handshake.java +@@ -12,8 +12,8 @@ + public class C00Handshake extends Packet + { + private int field_149600_a; +- private String field_149598_b; +- private int field_149599_c; ++ public String field_149598_b; // CraftBukkit private -> public ++ public int field_149599_c; // CraftBukkit private -> public + private EnumConnectionState field_149597_d; + private static final String __OBFID = "CL_00001372"; + +@@ -31,7 +31,7 @@ + public void readPacketData(PacketBuffer p_148837_1_) throws IOException + { + this.field_149600_a = p_148837_1_.readVarIntFromBuffer(); +- this.field_149598_b = p_148837_1_.readStringFromBuffer(255); ++ this.field_149598_b = p_148837_1_.readStringFromBuffer(Short.MAX_VALUE); // Spigot + this.field_149599_c = p_148837_1_.readUnsignedShort(); + this.field_149597_d = EnumConnectionState.func_150760_a(p_148837_1_.readVarIntFromBuffer()); + } diff --git a/patches/net/minecraft/network/play/client/C01PacketChatMessage.java.patch b/patches/net/minecraft/network/play/client/C01PacketChatMessage.java.patch new file mode 100644 index 0000000..bdddff2 --- /dev/null +++ b/patches/net/minecraft/network/play/client/C01PacketChatMessage.java.patch @@ -0,0 +1,14 @@ +--- ../src-base/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.java ++++ ../src-work/minecraft/net/minecraft/network/play/client/C01PacketChatMessage.java +@@ -52,4 +52,11 @@ + { + this.processPacket((INetHandlerPlayServer)p_148833_1_); + } ++ ++ // CraftBukkit start - make chat async ++ public boolean hasPriority() ++ { ++ return !this.field_149440_a.startsWith("/"); ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/network/play/client/C03PacketPlayer.java.patch b/patches/net/minecraft/network/play/client/C03PacketPlayer.java.patch new file mode 100644 index 0000000..be44d17 --- /dev/null +++ b/patches/net/minecraft/network/play/client/C03PacketPlayer.java.patch @@ -0,0 +1,28 @@ +--- ../src-base/minecraft/net/minecraft/network/play/client/C03PacketPlayer.java ++++ ../src-work/minecraft/net/minecraft/network/play/client/C03PacketPlayer.java +@@ -10,15 +10,17 @@ + + public class C03PacketPlayer extends Packet + { +- protected double field_149479_a; +- protected double field_149477_b; +- protected double field_149478_c; +- protected double field_149475_d; +- protected float field_149476_e; +- protected float field_149473_f; ++ // CraftBukkit start - protected -> public ++ public double field_149479_a; ++ public double field_149477_b; ++ public double field_149478_c; ++ public double field_149475_d; ++ public float field_149476_e; ++ public float field_149473_f; ++ // CraftBukkit end + protected boolean field_149474_g; +- protected boolean field_149480_h; +- protected boolean field_149481_i; ++ public boolean field_149480_h; // CraftBukkit - protected -> public ++ public boolean field_149481_i; + private static final String __OBFID = "CL_00001360"; + + public C03PacketPlayer() {} diff --git a/patches/net/minecraft/network/play/client/C0DPacketCloseWindow.java.patch b/patches/net/minecraft/network/play/client/C0DPacketCloseWindow.java.patch new file mode 100644 index 0000000..f9df09e --- /dev/null +++ b/patches/net/minecraft/network/play/client/C0DPacketCloseWindow.java.patch @@ -0,0 +1,16 @@ +--- ../src-base/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.java ++++ ../src-work/minecraft/net/minecraft/network/play/client/C0DPacketCloseWindow.java +@@ -15,11 +15,12 @@ + + public C0DPacketCloseWindow() {} + +- @SideOnly(Side.CLIENT) ++ // CraftBukkit start - Add constructor + public C0DPacketCloseWindow(int p_i45247_1_) + { + this.field_149556_a = p_i45247_1_; + } ++ // CraftBukkit end + + public void processPacket(INetHandlerPlayServer p_148833_1_) + { diff --git a/patches/net/minecraft/network/play/client/C17PacketCustomPayload.java.patch b/patches/net/minecraft/network/play/client/C17PacketCustomPayload.java.patch new file mode 100644 index 0000000..0d96dc1 --- /dev/null +++ b/patches/net/minecraft/network/play/client/C17PacketCustomPayload.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.java ++++ ../src-work/minecraft/net/minecraft/network/play/client/C17PacketCustomPayload.java +@@ -12,7 +12,7 @@ + public class C17PacketCustomPayload extends Packet + { + private String field_149562_a; +- private int field_149560_b; ++ public int field_149560_b; // CraftBukkit - private -> public + private byte[] field_149561_c; + private static final String __OBFID = "CL_00001356"; + diff --git a/patches/net/minecraft/network/play/server/S05PacketSpawnPosition.java.patch b/patches/net/minecraft/network/play/server/S05PacketSpawnPosition.java.patch new file mode 100644 index 0000000..b5b8fa6 --- /dev/null +++ b/patches/net/minecraft/network/play/server/S05PacketSpawnPosition.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.java ++++ ../src-work/minecraft/net/minecraft/network/play/server/S05PacketSpawnPosition.java +@@ -10,9 +10,9 @@ + + public class S05PacketSpawnPosition extends Packet + { +- private int field_149364_a; +- private int field_149362_b; +- private int field_149363_c; ++ public int field_149364_a; // CraftBukkit - private -> public ++ public int field_149362_b; // CraftBukkit - private -> public ++ public int field_149363_c; // CraftBukkit - private -> public + private static final String __OBFID = "CL_00001336"; + + public S05PacketSpawnPosition() {} diff --git a/patches/net/minecraft/network/play/server/S21PacketChunkData.java.patch b/patches/net/minecraft/network/play/server/S21PacketChunkData.java.patch new file mode 100644 index 0000000..9b57022 --- /dev/null +++ b/patches/net/minecraft/network/play/server/S21PacketChunkData.java.patch @@ -0,0 +1,54 @@ +--- ../src-base/minecraft/net/minecraft/network/play/server/S21PacketChunkData.java ++++ ../src-work/minecraft/net/minecraft/network/play/server/S21PacketChunkData.java +@@ -198,8 +198,10 @@ + if (aextendedblockstorage[l] != null && (!p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && (p_149269_2_ & 1 << l) != 0) + { + nibblearray = aextendedblockstorage[l].getMetadataArray(); +- System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); +- j += nibblearray.data.length; ++ // Spigot start ++ nibblearray.copyToByteArray(abyte, j); ++ j += nibblearray.getByteLength(); ++ // Spigot end + } + } + +@@ -208,8 +210,10 @@ + if (aextendedblockstorage[l] != null && (!p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && (p_149269_2_ & 1 << l) != 0) + { + nibblearray = aextendedblockstorage[l].getBlocklightArray(); +- System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); +- j += nibblearray.data.length; ++ // Spigot start ++ nibblearray.copyToByteArray(abyte, j); ++ j += nibblearray.getByteLength(); ++ // Spigot end + } + } + +@@ -220,8 +224,10 @@ + if (aextendedblockstorage[l] != null && (!p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && (p_149269_2_ & 1 << l) != 0) + { + nibblearray = aextendedblockstorage[l].getSkylightArray(); +- System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); +- j += nibblearray.data.length; ++ // Spigot start ++ nibblearray.copyToByteArray(abyte, j); ++ j += nibblearray.getByteLength(); ++ // Spigot end + } + } + } +@@ -233,8 +239,10 @@ + if (aextendedblockstorage[l] != null && (!p_149269_1_ || !aextendedblockstorage[l].isEmpty()) && aextendedblockstorage[l].getBlockMSBArray() != null && (p_149269_2_ & 1 << l) != 0) + { + nibblearray = aextendedblockstorage[l].getBlockMSBArray(); +- System.arraycopy(nibblearray.data, 0, abyte, j, nibblearray.data.length); +- j += nibblearray.data.length; ++ // Spigot start ++ nibblearray.copyToByteArray(abyte, j); ++ j += nibblearray.getByteLength(); ++ // Spigot end + } + } + } diff --git a/patches/net/minecraft/network/play/server/S26PacketMapChunkBulk.java.patch b/patches/net/minecraft/network/play/server/S26PacketMapChunkBulk.java.patch new file mode 100644 index 0000000..893b190 --- /dev/null +++ b/patches/net/minecraft/network/play/server/S26PacketMapChunkBulk.java.patch @@ -0,0 +1,117 @@ +--- ../src-base/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.java ++++ ../src-work/minecraft/net/minecraft/network/play/server/S26PacketMapChunkBulk.java +@@ -24,10 +24,18 @@ + private byte[][] field_149260_f; + private int field_149261_g; + private boolean field_149267_h; +- private static byte[] field_149268_i = new byte[0]; +- private static final String __OBFID = "CL_00001306"; +- private int maxLen = 0; +- private Semaphore deflateGate; ++ private byte[] field_149268_i = new byte[0]; // CraftBukkit - remove static ++ // CraftBukkit start ++ static final ThreadLocal localDeflater = new ThreadLocal() ++ { ++ @Override ++ protected Deflater initialValue() ++ { ++ // Don't use higher compression level, slows things down too much ++ return new Deflater(6); ++ } ++ }; ++ // CraftBukkit end + + public S26PacketMapChunkBulk() {} + +@@ -46,6 +54,15 @@ + { + Chunk chunk = (Chunk)p_i45197_1_.get(k); + S21PacketChunkData.Extracted extracted = S21PacketChunkData.func_149269_a(chunk, true, 65535); ++ ++ if (field_149268_i.length < j + extracted.field_150282_a.length) ++ { ++ byte[] abyte = new byte[j + extracted.field_150282_a.length]; ++ System.arraycopy(field_149268_i, 0, abyte, 0, field_149268_i.length); ++ field_149268_i = abyte; ++ } ++ ++ System.arraycopy(extracted.field_150282_a, 0, field_149268_i, j, extracted.field_150282_a.length); + j += extracted.field_150282_a.length; + this.field_149266_a[k] = chunk.xPosition; + this.field_149264_b[k] = chunk.zPosition; +@@ -53,34 +70,36 @@ + this.field_149262_d[k] = extracted.field_150281_c; + this.field_149260_f[k] = extracted.field_150282_a; + } +- this.deflateGate = new Semaphore(1); +- maxLen = j; +- } +- +- private void deflate() +- { +- byte[] data = new byte[maxLen]; +- int offset = 0; +- for (int x = 0; x < field_149260_f.length; x++) +- { +- System.arraycopy(field_149260_f[x], 0, data, offset, field_149260_f[x].length); +- offset += field_149260_f[x].length; +- } ++ /* CraftBukkit start - Moved to compress() + Deflater deflater = new Deflater(-1); + +- try +- { +- deflater.setInput(data, 0, data.length); ++ try { ++ deflater.setInput(buildBuffer, 0, j); + deflater.finish(); +- byte[] deflated = new byte[data.length]; +- this.field_149261_g = deflater.deflate(deflated); +- this.field_149263_e = deflated; ++ this.buffer = new byte[j]; ++ this.size = deflater.deflate(this.buffer); ++ } finally { ++ deflater.end(); + } +- finally ++ */ ++ } ++ ++ // Add compression method ++ public void compress() ++ { ++ if (this.field_149263_e != null) + { +- deflater.end(); ++ return; + } ++ ++ Deflater deflater = localDeflater.get(); ++ deflater.reset(); ++ deflater.setInput(this.field_149268_i); ++ deflater.finish(); ++ this.field_149263_e = new byte[this.field_149268_i.length + 100]; ++ this.field_149261_g = deflater.deflate(this.field_149263_e); + } ++ // CraftBukkit end + + public static int func_149258_c() + { +@@ -155,16 +174,7 @@ + + public void writePacketData(PacketBuffer p_148840_1_) throws IOException + { +- if (this.field_149263_e == null) +- { +- deflateGate.acquireUninterruptibly(); +- if (this.field_149263_e == null) +- { +- deflate(); +- } +- deflateGate.release(); +- } +- ++ compress(); // CraftBukkit + p_148840_1_.writeShort(this.field_149266_a.length); + p_148840_1_.writeInt(this.field_149261_g); + p_148840_1_.writeBoolean(this.field_149267_h); diff --git a/patches/net/minecraft/network/rcon/RConConsoleSource.java.patch b/patches/net/minecraft/network/rcon/RConConsoleSource.java.patch new file mode 100644 index 0000000..a77c109 --- /dev/null +++ b/patches/net/minecraft/network/rcon/RConConsoleSource.java.patch @@ -0,0 +1,16 @@ +--- ../src-base/minecraft/net/minecraft/network/rcon/RConConsoleSource.java ++++ ../src-work/minecraft/net/minecraft/network/rcon/RConConsoleSource.java +@@ -25,6 +25,13 @@ + return new ChatComponentText(this.getCommandSenderName()); + } + ++ // CraftBukkit start - Send a String ++ public void sendMessage(String message) ++ { ++ this.buffer.append(message); ++ } ++ // CraftBukkit end ++ + public void addChatMessage(IChatComponent p_145747_1_) + { + this.buffer.append(p_145747_1_.getUnformattedText()); diff --git a/patches/net/minecraft/potion/Potion.java.patch b/patches/net/minecraft/potion/Potion.java.patch new file mode 100644 index 0000000..4303e05 --- /dev/null +++ b/patches/net/minecraft/potion/Potion.java.patch @@ -0,0 +1,79 @@ +--- ../src-base/minecraft/net/minecraft/potion/Potion.java ++++ ../src-work/minecraft/net/minecraft/potion/Potion.java +@@ -17,6 +17,12 @@ + import net.minecraft.util.DamageSource; + import net.minecraft.util.StringUtils; + ++// CraftBukkit start ++import net.minecraft.entity.projectile.EntityPotion; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; ++// CraftBukkit end ++ + public class Potion + { + public static final Potion[] potionTypes = new Potion[32]; +@@ -78,6 +84,7 @@ + } + + this.liquidColor = p_i1573_3_; ++ org.bukkit.potion.PotionEffectType.registerPotionEffectType(new org.bukkit.craftbukkit.potion.CraftPotionEffectType(this)); // CraftBukkit + } + + protected Potion setIconIndex(int p_76399_1_, int p_76399_2_) +@@ -97,14 +104,14 @@ + { + if (p_76394_1_.getHealth() < p_76394_1_.getMaxHealth()) + { +- p_76394_1_.heal(1.0F); ++ p_76394_1_.heal(1.0F, RegainReason.MAGIC_REGEN); // CraftBukkit + } + } + else if (this.id == poison.id) + { + if (p_76394_1_.getHealth() > 1.0F) + { +- p_76394_1_.attackEntityFrom(DamageSource.magic, 1.0F); ++ p_76394_1_.attackEntityFrom(CraftEventFactory.POISON, 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON + } + } + else if (this.id == wither.id) +@@ -131,12 +138,19 @@ + } + else + { +- p_76394_1_.heal((float)Math.max(4 << p_76394_2_, 0)); ++ p_76394_1_.heal((float)Math.max(4 << p_76394_2_, 0), RegainReason.MAGIC); // CraftBukkit + } + } + + public void affectEntity(EntityLivingBase p_76402_1_, EntityLivingBase p_76402_2_, int p_76402_3_, double p_76402_4_) + { ++ // CraftBukkit start - Delegate; we need EntityPotion ++ applyInstantEffect(p_76402_1_, p_76402_2_, p_76402_3_, p_76402_4_, null); ++ } ++ ++ public void applyInstantEffect(EntityLivingBase p_76402_1_, EntityLivingBase p_76402_2_, int p_76402_3_, double p_76402_4_, EntityPotion potion) ++ { ++ // CraftBukkit end + int j; + + if ((this.id != heal.id || p_76402_2_.isEntityUndead()) && (this.id != harm.id || !p_76402_2_.isEntityUndead())) +@@ -151,14 +165,15 @@ + } + else + { +- p_76402_2_.attackEntityFrom(DamageSource.causeIndirectMagicDamage(p_76402_2_, p_76402_1_), (float)j); ++ // CraftBukkit - The "damager" needs to be the potion ++ p_76402_2_.attackEntityFrom(DamageSource.causeIndirectMagicDamage(potion != null ? potion : p_76402_2_, p_76402_1_), (float)j); + } + } + } + else + { + j = (int)(p_76402_4_ * (double)(4 << p_76402_3_) + 0.5D); +- p_76402_2_.heal((float)j); ++ p_76402_2_.heal((float)j, RegainReason.MAGIC); // CraftBukkit + } + } + diff --git a/patches/net/minecraft/scoreboard/ServerScoreboard.java.patch b/patches/net/minecraft/scoreboard/ServerScoreboard.java.patch new file mode 100644 index 0000000..29cf228 --- /dev/null +++ b/patches/net/minecraft/scoreboard/ServerScoreboard.java.patch @@ -0,0 +1,138 @@ +--- ../src-base/minecraft/net/minecraft/scoreboard/ServerScoreboard.java ++++ ../src-work/minecraft/net/minecraft/scoreboard/ServerScoreboard.java +@@ -32,7 +32,7 @@ + + if (this.field_96553_b.contains(p_96536_1_.func_96645_d())) + { +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3CPacketUpdateScore(p_96536_1_, 0)); ++ this.sendAll(new S3CPacketUpdateScore(p_96536_1_, 0)); // CraftBukkit - Internal packet method + } + + this.func_96551_b(); +@@ -41,7 +41,7 @@ + public void func_96516_a(String p_96516_1_) + { + super.func_96516_a(p_96516_1_); +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3CPacketUpdateScore(p_96516_1_)); ++ this.sendAll(new S3CPacketUpdateScore(p_96516_1_)); // CraftBukkit - Internal packet method + this.func_96551_b(); + } + +@@ -54,7 +54,7 @@ + { + if (this.func_96552_h(scoreobjective1) > 0) + { +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3DPacketDisplayScoreboard(p_96530_1_, p_96530_2_)); ++ this.sendAll(new S3DPacketDisplayScoreboard(p_96530_1_, p_96530_2_)); // CraftBukkit - Internal packet method + } + else + { +@@ -66,7 +66,7 @@ + { + if (this.field_96553_b.contains(p_96530_2_)) + { +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3DPacketDisplayScoreboard(p_96530_1_, p_96530_2_)); ++ this.sendAll(new S3DPacketDisplayScoreboard(p_96530_1_, p_96530_2_)); // CraftBukkit - Internal packet method + } + else + { +@@ -82,7 +82,7 @@ + if (super.func_151392_a(p_151392_1_, p_151392_2_)) + { + ScorePlayerTeam scoreplayerteam = this.getTeam(p_151392_2_); +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3EPacketTeams(scoreplayerteam, Arrays.asList(new String[] {p_151392_1_}), 3)); ++ this.sendAll(new S3EPacketTeams(scoreplayerteam, Arrays.asList(new String[] { p_151392_1_}), 3)); // CraftBukkit - Internal packet method + this.func_96551_b(); + return true; + } +@@ -95,7 +95,7 @@ + public void removePlayerFromTeam(String p_96512_1_, ScorePlayerTeam p_96512_2_) + { + super.removePlayerFromTeam(p_96512_1_, p_96512_2_); +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3EPacketTeams(p_96512_2_, Arrays.asList(new String[] {p_96512_1_}), 4)); ++ this.sendAll(new S3EPacketTeams(p_96512_2_, Arrays.asList(new String[] { p_96512_1_}), 4)); // CraftBukkit - Internal packet method + this.func_96551_b(); + } + +@@ -111,7 +111,7 @@ + + if (this.field_96553_b.contains(p_96532_1_)) + { +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3BPacketScoreboardObjective(p_96532_1_, 2)); ++ this.sendAll(new S3BPacketScoreboardObjective(p_96532_1_, 2)); // CraftBukkit - Internal packet method + } + + this.func_96551_b(); +@@ -132,21 +132,21 @@ + public void broadcastTeamCreated(ScorePlayerTeam p_96523_1_) + { + super.broadcastTeamCreated(p_96523_1_); +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3EPacketTeams(p_96523_1_, 0)); ++ this.sendAll(new S3EPacketTeams(p_96523_1_, 0)); // CraftBukkit - Internal packet method + this.func_96551_b(); + } + + public void broadcastTeamRemoved(ScorePlayerTeam p_96538_1_) + { + super.broadcastTeamRemoved(p_96538_1_); +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3EPacketTeams(p_96538_1_, 2)); ++ this.sendAll(new S3EPacketTeams(p_96538_1_, 2)); // CraftBukkit - Internal packet method + this.func_96551_b(); + } + + public void func_96513_c(ScorePlayerTeam p_96513_1_) + { + super.func_96513_c(p_96513_1_); +- this.scoreboardMCServer.getConfigurationManager().sendPacketToAllPlayers(new S3EPacketTeams(p_96513_1_, 1)); ++ this.sendAll(new S3EPacketTeams(p_96513_1_, 1)); // CraftBukkit - Internal packet method + this.func_96551_b(); + } + +@@ -194,7 +194,13 @@ + + while (iterator.hasNext()) + { +- EntityPlayerMP entityplayermp = (EntityPlayerMP)iterator.next(); ++ EntityPlayerMP entityplayermp = (EntityPlayerMP) iterator.next(); ++ ++ if (entityplayermp.getBukkitEntity().getScoreboard().getHandle() != this) ++ { ++ continue; // CraftBukkit - Only players on this board ++ } ++ + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) +@@ -230,7 +236,13 @@ + + while (iterator.hasNext()) + { +- EntityPlayerMP entityplayermp = (EntityPlayerMP)iterator.next(); ++ EntityPlayerMP entityplayermp = (EntityPlayerMP) iterator.next(); ++ ++ if (entityplayermp.getBukkitEntity().getScoreboard().getHandle() != this) ++ { ++ continue; // CraftBukkit - Only players on this board ++ } ++ + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) +@@ -257,4 +269,17 @@ + + return i; + } ++ ++ // CraftBukkit start - Send to players ++ private void sendAll(Packet packet) ++ { ++ for (EntityPlayerMP entityplayermp : (List) this.scoreboardMCServer.getConfigurationManager().playerEntityList) ++ { ++ if (entityplayermp.getBukkitEntity().getScoreboard().getHandle() == this) ++ { ++ entityplayermp.playerNetServerHandler.sendPacket(packet); ++ } ++ } ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/server/MinecraftServer.java.patch b/patches/net/minecraft/server/MinecraftServer.java.patch new file mode 100644 index 0000000..5dc143d --- /dev/null +++ b/patches/net/minecraft/server/MinecraftServer.java.patch @@ -0,0 +1,1192 @@ +--- ../src-base/minecraft/net/minecraft/server/MinecraftServer.java ++++ ../src-work/minecraft/net/minecraft/server/MinecraftServer.java +@@ -33,6 +33,8 @@ + import java.util.Random; + import java.util.UUID; + import java.util.concurrent.Callable; ++import java.util.logging.Level; ++ + import javax.imageio.ImageIO; + import net.minecraft.command.CommandBase; + import net.minecraft.command.ICommandManager; +@@ -50,6 +52,7 @@ + import net.minecraft.profiler.PlayerUsageSnooper; + import net.minecraft.profiler.Profiler; + import net.minecraft.server.dedicated.DedicatedServer; ++import net.minecraft.server.dedicated.PropertyManager; + import net.minecraft.server.gui.IUpdatePlayerListBox; + import net.minecraft.server.management.PlayerProfileCache; + import net.minecraft.server.management.ServerConfigurationManager; +@@ -80,18 +83,49 @@ + import net.minecraftforge.common.MinecraftForge; + import net.minecraftforge.event.world.WorldEvent; + ++// CraftBukkit start ++import java.io.IOException; ++ ++import jline.console.ConsoleReader; ++import joptsimple.OptionSet; ++import net.minecraft.world.chunk.storage.AnvilSaveHandler; ++import org.bukkit.World.Environment; ++import org.bukkit.craftbukkit.SpigotTimings; // Spigot ++import org.bukkit.craftbukkit.util.Waitable; ++import org.bukkit.event.server.RemoteServerCommandEvent; ++import org.bukkit.event.world.WorldSaveEvent; ++// CraftBukkit end ++// Cauldron start ++import java.util.Map; ++import java.lang.reflect.Constructor; ++import joptsimple.OptionParser; ++import cpw.mods.fml.common.asm.transformers.SideTransformer; ++import net.minecraft.command.ServerCommand; ++import net.minecraft.tileentity.TileEntity; ++import net.minecraft.world.WorldProvider; ++import net.minecraftforge.cauldron.CauldronUtils; ++import net.minecraftforge.cauldron.configuration.CauldronConfig; ++import net.minecraftforge.cauldron.configuration.TileEntityConfig; ++import net.minecraftforge.common.util.EnumHelper; ++ ++import org.bukkit.configuration.ConfigurationSection; ++import org.bukkit.configuration.file.YamlConfiguration; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.block.CraftBlock; ++// Cauldron end ++ + public abstract class MinecraftServer implements ICommandSender, Runnable, IPlayerUsage + { + private static final Logger logger = LogManager.getLogger(); + public static final File field_152367_a = new File("usercache.json"); + private static MinecraftServer mcServer; +- private final ISaveFormat anvilConverterForAnvilFile; ++ public ISaveFormat anvilConverterForAnvilFile; // CraftBukkit - private final -> public + private final PlayerUsageSnooper usageSnooper = new PlayerUsageSnooper("server", this, getSystemTimeMillis()); +- private final File anvilFile; ++ public File anvilFile; // CraftBukkit - private final -> public + private final List tickables = new ArrayList(); + private final ICommandManager commandManager; + public final Profiler theProfiler = new Profiler(); +- private final NetworkSystem field_147144_o; ++ private NetworkSystem field_147144_o; // Spigot + private final ServerStatusResponse field_147147_p = new ServerStatusResponse(); + private final Random field_147146_q = new Random(); + @SideOnly(Side.SERVER) +@@ -135,8 +169,39 @@ + private long field_147142_T = 0L; + private final GameProfileRepository field_152365_W; + private final PlayerProfileCache field_152366_X; ++ // CraftBukkit start ++ public List worlds = new ArrayList(); ++ public org.bukkit.craftbukkit.CraftServer server; ++ public static OptionSet options; // Cauldron ++ public org.bukkit.command.ConsoleCommandSender console; ++ public org.bukkit.command.RemoteConsoleCommandSender remoteConsole; ++ public ConsoleReader reader; ++ public static int currentTick = (int)(System.currentTimeMillis() / 50); ++ public final Thread primaryThread; ++ public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); ++ public int autosavePeriod; ++ // CraftBukkit end ++ // Spigot start ++ private static final int TPS = 20; ++ private static final int TICK_TIME = 1000000000 / TPS; ++ public final double[] recentTps = new double[ 3 ]; ++ // Spigot end ++ // Cauldron start ++ public static CauldronConfig cauldronConfig; ++ public static TileEntityConfig tileEntityConfig; ++ public static YamlConfiguration configuration; ++ public static YamlConfiguration commandsConfiguration; ++ public static File configFile; ++ public static File commandFile; ++ public static double currentTps = 0; ++ public static boolean useJline = true; ++ public static boolean useConsole = true; ++ public static boolean callingForgeTick = false; ++ public static List> bannedTileEntityUpdates = new ArrayList>(); ++ // Cauldron end + private static final String __OBFID = "CL_00001462"; + ++ // Cauldron start - IntegratedServer requires this + public MinecraftServer(File p_i45281_1_, Proxy p_i45281_2_) + { + this.field_152366_X = new PlayerProfileCache(this, field_152367_a); +@@ -149,10 +214,68 @@ + this.field_152364_T = new YggdrasilAuthenticationService(p_i45281_2_, UUID.randomUUID().toString()); + this.field_147143_S = this.field_152364_T.createMinecraftSessionService(); + this.field_152365_W = this.field_152364_T.createProfileRepository(); ++ this.primaryThread = new Thread(this, "Server thread"); // CraftBukkit ++ this.cauldronConfig = new CauldronConfig("cauldron.yml", "cauldron"); ++ this.tileEntityConfig = new TileEntityConfig("tileentities.yml", "cauldron_te"); + } ++ // Cauldron end + +- protected abstract boolean startServer() throws IOException; ++ public MinecraftServer(OptionSet options, Proxy proxy) // CraftBukkit - signature file -> OptionSet ++ { ++ this.field_152366_X = new PlayerProfileCache(this, field_152367_a); ++ mcServer = this; ++ this.serverProxy = proxy; ++ // this.anvilFile = p_i45281_1_; // CraftBukkit ++ // this.field_147144_o = new NetworkSystem(this); // Spigot ++ this.commandManager = new ServerCommandManager(); ++ // this.anvilConverterForAnvilFile = new AnvilSaveConverter(p_i45281_1_); // CraftBukkit - moved to DedicatedServer.init ++ this.field_152364_T = new YggdrasilAuthenticationService(proxy, UUID.randomUUID().toString()); ++ this.field_147143_S = this.field_152364_T.createMinecraftSessionService(); ++ this.field_152365_W = this.field_152364_T.createProfileRepository(); ++ // Cauldron start ++ this.cauldronConfig = new CauldronConfig("cauldron.yml", "cauldron"); ++ this.tileEntityConfig = new TileEntityConfig("tileentities.yml", "cauldron_te"); ++ // Cauldron end ++ // CraftBukkit start ++ this.options = options; ++ // Try to see if we're actually running in a terminal, disable jline if not ++ if (System.console() == null) ++ { ++ System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); ++ this.useJline = false; // Cauldron ++ } + ++ try ++ { ++ this.reader = new ConsoleReader(System.in, System.out); ++ this.reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators ++ } ++ catch (Throwable e) ++ { ++ try ++ { ++ // Try again with jline disabled for Windows users without C++ 2008 Redistributable ++ System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); ++ System.setProperty("user.language", "en"); ++ this.useJline = false; // Cauldron ++ this.reader = new ConsoleReader(System.in, System.out); ++ this.reader.setExpandEvents(false); ++ } ++ catch (IOException ex) ++ { ++ logger.warn((String) null, ex); ++ } ++ } ++ net.minecraftforge.cauldron.CauldronHooks.enableThreadContentionMonitoring(); ++ Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this)); ++ primaryThread = new Thread(this, "Server thread"); // Moved from main ++ } ++ ++ public abstract PropertyManager getPropertyManager(); ++ // CraftBukkit end ++ ++ protected abstract boolean startServer() throws java.net.UnknownHostException; // CraftBukkit - throws UnknownHostException ++ + protected void convertMapIfNeeded(String p_71237_1_) + { + if (this.getActiveAnvilConverter().isOldMapFormat(p_71237_1_)) +@@ -172,6 +295,7 @@ + MinecraftServer.logger.info("Converting... " + p_73718_1_ + "%"); + } + } ++ + @SideOnly(Side.CLIENT) + public void resetProgressAndMessage(String p_73721_1_) {} + @SideOnly(Side.CLIENT) +@@ -195,10 +319,17 @@ + + protected void loadAllWorlds(String p_71247_1_, String p_71247_2_, long p_71247_3_, WorldType p_71247_5_, String p_71247_6_) + { ++ // Cauldron start - register vanilla server commands ++ ServerCommandManager vanillaCommandManager = (ServerCommandManager)this.getCommandManager(); ++ vanillaCommandManager.registerVanillaCommands(); ++ // Cauldron end + this.convertMapIfNeeded(p_71247_1_); + this.setUserMessage("menu.loadingLevel"); +- ISaveHandler isavehandler = this.anvilConverterForAnvilFile.getSaveLoader(p_71247_1_, true); +- WorldInfo worldinfo = isavehandler.loadWorldInfo(); ++ // Cauldron start - SaveHandler/WorldInfo below are not used and must be disabled to prevent FML receiving different handlers for overworld ++ //ISaveHandler isavehandler = this.anvilConverterForAnvilFile.getSaveLoader(p_71247_1_, true); ++ //WorldInfo worldinfo = isavehandler.loadWorldInfo(); ++ // Cauldron end ++ /* CraftBukkit start - Removed worldsettings + WorldSettings worldsettings; + + if (worldinfo == null) +@@ -215,11 +346,79 @@ + { + worldsettings.enableBonusChest(); + } ++ // */ + +- WorldServer overWorld = (isDemo() ? new DemoWorldServer(this, isavehandler, p_71247_2_, 0, theProfiler) : new WorldServer(this, isavehandler, p_71247_2_, 0, worldsettings, theProfiler)); +- for (int dim : DimensionManager.getStaticDimensionIDs()) ++ WorldSettings worldsettings = new WorldSettings(p_71247_3_, this.getGameType(), this.canStructuresSpawn(), this.isHardcore(), p_71247_5_); ++ worldsettings.func_82750_a(p_71247_6_); ++ WorldServer world; ++ ++ // Cauldron - overworld generator is handled in World after plugins load ++ WorldServer overWorld = (isDemo() ? new DemoWorldServer(this, new AnvilSaveHandler(server.getWorldContainer(), p_71247_2_, true), p_71247_2_, 0, theProfiler) : new WorldServer(this, new AnvilSaveHandler(server.getWorldContainer(), p_71247_2_, true), p_71247_2_, 0, worldsettings, theProfiler, Environment.getEnvironment(0), null)); ++ ++ for (int dimension : DimensionManager.getStaticDimensionIDs()) + { +- WorldServer world = (dim == 0 ? overWorld : new WorldServerMulti(this, isavehandler, p_71247_2_, dim, worldsettings, overWorld, theProfiler)); ++ String worldType = ""; ++ String name = ""; ++ String oldName = ""; ++ org.bukkit.generator.ChunkGenerator gen = null; ++ // Cauldron start ++ Environment env = Environment.getEnvironment(dimension); ++ if (dimension != 0) ++ { ++ if ((dimension == -1 && !this.getAllowNether()) || (dimension == 1 && !this.server.getAllowEnd())) ++ continue; ++ ++ if (env == null) ++ { ++ WorldProvider provider = WorldProvider.getProviderForDimension(dimension); ++ worldType = provider.getClass().getSimpleName().toLowerCase(); ++ worldType = worldType.replace("worldprovider", ""); ++ oldName = "world_" + worldType.toLowerCase(); ++ worldType = worldType.replace("provider", ""); ++ env = Environment.getEnvironment(DimensionManager.getProviderType(provider.getClass())); ++ name = provider.getSaveFolder(); ++ if (name == null) name = "DIM0"; ++ } ++ else ++ { ++ worldType = env.toString().toLowerCase(); ++ name = "DIM" + dimension; ++ oldName = p_71247_1_ + "_" + worldType; ++ oldName = oldName.replaceAll(" ", "_"); ++ } ++ ++ // check if the world is enabled or not ++ if (!configuration.isBoolean("world-settings." + worldType + ".enabled")) { ++ configuration.set("world-settings." + worldType + ".enabled", true); ++ } ++ boolean enabled = configuration.getBoolean("world-settings." + worldType + ".enabled"); ++ try { ++ configuration.save(MinecraftServer.configFile); ++ } catch (IOException e) { ++ e.printStackTrace(); ++ } ++ if (!enabled) ++ continue; ++ // end world enabled check ++ ++ gen = this.server.getGenerator(name); ++ worldsettings = new WorldSettings(p_71247_3_, this.getGameType(), this.canStructuresSpawn(), this.isHardcore(), p_71247_5_); ++ worldsettings.func_82750_a(p_71247_6_); ++ ++ CauldronUtils.migrateWorlds(worldType, oldName, p_71247_1_, name); ++ ++ this.setUserMessage(name); ++ } ++ ++ world = (dimension == 0 ? overWorld : new WorldServerMulti(this, new AnvilSaveHandler(server.getWorldContainer(), name, true), name, dimension, worldsettings, overWorld, this.theProfiler, env, gen)); ++ // Cauldron end ++ if (gen != null) ++ { ++ world.getWorld().getPopulators().addAll(gen.getDefaultPopulators(world.getWorld())); ++ } ++ ++ this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard()); ++ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(world.getWorld())); + world.addWorldAccess(new WorldManager(this, world)); + + if (!this.isSinglePlayer()) +@@ -227,12 +426,14 @@ + world.getWorldInfo().setGameType(this.getGameType()); + } + +- MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world)); ++ this.serverConfigManager.setPlayerManager(this.worlds.toArray(new WorldServer[this.worlds.size()])); ++ // CraftBukkit end ++ MinecraftForge.EVENT_BUS.post(new WorldEvent.Load((World)world)); // Forge + } +- +- this.serverConfigManager.setPlayerManager(new WorldServer[]{ overWorld }); + this.func_147139_a(this.func_147135_j()); + this.initialWorldChunkLoad(); ++ CraftBlock.dumpMaterials(); ++ // Cauldron end + } + + protected void initialWorldChunkLoad() +@@ -244,9 +445,12 @@ + int i = 0; + this.setUserMessage("menu.generatingTerrain"); + byte b0 = 0; ++ // Cauldron start - we now handle CraftBukkit's keepSpawnInMemory logic in DimensionManager. Prevents crashes with mods such as DivineRPG and speeds up server startup time by a ton. + logger.info("Preparing start region for level " + b0); + WorldServer worldserver = this.worldServers[b0]; + ChunkCoordinates chunkcoordinates = worldserver.getSpawnPoint(); ++ boolean before = worldserver.theChunkProviderServer.loadChunkOnProvideRequest; ++ worldserver.theChunkProviderServer.loadChunkOnProvideRequest = true; + long j = getSystemTimeMillis(); + + for (int k = -192; k <= 192 && this.isServerRunning(); k += 16) +@@ -265,7 +469,8 @@ + worldserver.theChunkProviderServer.loadChunk(chunkcoordinates.posX + k >> 4, chunkcoordinates.posZ + l >> 4); + } + } +- ++ worldserver.theChunkProviderServer.loadChunkOnProvideRequest = before; ++ // Cauldron end + this.clearCurrentTask(); + } + +@@ -292,19 +497,17 @@ + { + this.currentTask = null; + this.percentDone = 0; ++ this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD); // CraftBukkit + } + +- protected void saveAllWorlds(boolean p_71267_1_) ++ protected void saveAllWorlds(boolean p_71267_1_) throws MinecraftException // CraftBukkit - added throws + { + if (!this.worldIsBeingDeleted) + { +- WorldServer[] aworldserver = this.worldServers; +- if (aworldserver == null) return; //Forge: Just in case, NPE protection as it has been encountered. +- int i = aworldserver.length; +- +- for (int j = 0; j < i; ++j) ++ // CraftBukkit start ++ for (int j = 0; j < this.worlds.size(); ++j) + { +- WorldServer worldserver = aworldserver[j]; ++ WorldServer worldserver = this.worlds.get(j); + + if (worldserver != null) + { +@@ -313,25 +516,41 @@ + logger.info("Saving chunks for level \'" + worldserver.getWorldInfo().getWorldName() + "\'/" + worldserver.provider.getDimensionName()); + } + +- try ++ worldserver.saveAllChunks(true, (IProgressUpdate) null); ++ worldserver.flush(); ++ WorldSaveEvent event = new WorldSaveEvent(worldserver.getWorld()); ++ this.server.getPluginManager().callEvent(event); ++ // Cauldron start - save world configs ++ if (worldserver.cauldronConfig != null) + { +- worldserver.saveAllChunks(true, (IProgressUpdate)null); ++ worldserver.cauldronConfig.save(); + } +- catch (MinecraftException minecraftexception) ++ if (worldserver.tileentityConfig != null) + { +- logger.warn(minecraftexception.getMessage()); ++ worldserver.tileentityConfig.save(); + } ++ // Cauldron end + } + } ++ ++ // CraftBukkit end + } + } + +- public void stopServer() ++ public void stopServer() throws MinecraftException // CraftBukkit - added throws + { + if (!this.worldIsBeingDeleted && Loader.instance().hasReachedState(LoaderState.SERVER_STARTED) && !serverStopped) // make sure the save is valid and we don't save twice + { + logger.info("Stopping server"); + ++ // CraftBukkit start ++ if (this.server != null) ++ { ++ this.server.disablePlugins(); ++ } ++ ++ // CraftBukkit end ++ + if (this.func_147137_ag() != null) + { + this.func_147137_ag().terminateEndpoints(); +@@ -347,7 +566,14 @@ + if (this.worldServers != null) + { + logger.info("Saving worlds"); +- this.saveAllWorlds(false); ++ try ++ { ++ this.saveAllWorlds(false); ++ } ++ catch (MinecraftException e) ++ { ++ e.printStackTrace(); ++ } + + for (int i = 0; i < this.worldServers.length; ++i) + { +@@ -380,6 +606,13 @@ + this.serverRunning = false; + } + ++ // Spigot Start ++ private static double calcTps(double avg, double exp, double tps) ++ { ++ return (avg * exp) + (tps * (1 - exp)); ++ } ++ // Spigot End ++ + public void run() + { + try +@@ -392,45 +625,41 @@ + this.field_147147_p.func_151315_a(new ChatComponentText(this.motd)); + this.field_147147_p.func_151321_a(new ServerStatusResponse.MinecraftProtocolVersionIdentifier("1.7.10", 5)); + this.func_147138_a(this.field_147147_p); ++ DedicatedServer.allowPlayerLogins = true; // Cauldron - server is ready, allow player logins ++ // Spigot start ++ Arrays.fill(recentTps, 20); ++ long lastTick = 0, catchupTime = 0, curTime, wait; + + while (this.serverRunning) + { +- long j = getSystemTimeMillis(); +- long k = j - i; ++ curTime = System.nanoTime(); ++ wait = TICK_TIME - (curTime - lastTick) - catchupTime; + +- if (k > 2000L && i - this.timeOfLastWarning >= 15000L) ++ if (wait > 0) + { +- logger.warn("Can\'t keep up! Did the system time change, or is the server overloaded? Running {}ms behind, skipping {} tick(s)", new Object[] {Long.valueOf(k), Long.valueOf(k / 50L)}); +- k = 2000L; +- this.timeOfLastWarning = i; ++ Thread.sleep(wait / 1000000); ++ catchupTime = 0; ++ continue; + } +- +- if (k < 0L) ++ else + { +- logger.warn("Time ran backwards! Did the system time change?"); +- k = 0L; ++ catchupTime = Math.min(1000000000, Math.abs(wait)); + } + +- l += k; +- i = j; +- +- if (this.worldServers[0].areAllPlayersAsleep()) ++ if (MinecraftServer.currentTick++ % 100 == 0) + { +- this.tick(); +- l = 0L; ++ currentTps = 1E9 / (curTime - lastTick); ++ recentTps[0] = calcTps(recentTps[0], 0.92, currentTps); // 1/exp(5sec/1min) ++ recentTps[1] = calcTps(recentTps[1], 0.9835, currentTps); // 1/exp(5sec/5min) ++ recentTps[2] = calcTps(recentTps[2], 0.9945, currentTps); // 1/exp(5sec/15min) + } +- else +- { +- while (l > 50L) +- { +- l -= 50L; +- this.tick(); +- } +- } + +- Thread.sleep(Math.max(1L, 50L - l)); ++ lastTick = curTime; ++ this.tick(); + this.serverIsRunning = true; + } ++ ++ // Spigot end + FMLCommonHandler.instance().handleServerStopping(); + FMLCommonHandler.instance().expectServerStopped(); // has to come before finalTick to avoid race conditions + } +@@ -448,6 +677,14 @@ + catch (Throwable throwable1) + { + logger.error("Encountered an unexpected exception", throwable1); ++ ++ // Spigot Start ++ if (throwable1.getCause() != null) ++ { ++ logger.error("\tCause of unexpected exception was", throwable1.getCause()); ++ } ++ ++ // Spigot End + CrashReport crashreport = null; + + if (throwable1 instanceof ReportedException) +@@ -477,6 +714,7 @@ + { + try + { ++ org.spigotmc.WatchdogThread.doStop(); // Spigot + this.stopServer(); + this.serverStopped = true; + } +@@ -486,6 +724,16 @@ + } + finally + { ++ // CraftBukkit start - Restore terminal to original settings ++ try ++ { ++ this.reader.getTerminal().restore(); ++ } ++ catch (Exception e) ++ { ++ } ++ ++ // CraftBukkit end + FMLCommonHandler.instance().handleServerStopped(); + this.serverStopped = true; + this.systemExitNow(); +@@ -532,8 +780,11 @@ + + public void tick() + { ++ SpigotTimings.serverTickTimer.startTiming(); // Spigot + long i = System.nanoTime(); ++ callingForgeTick = true; // Cauldron start - handle loadOnProviderRequests during forge tick event + FMLCommonHandler.instance().onPreServerTick(); ++ callingForgeTick = false; // Cauldron end + ++this.tickCounter; + + if (this.startProfiling) +@@ -562,12 +813,21 @@ + this.field_147147_p.func_151318_b().func_151330_a(agameprofile); + } + +- if (this.tickCounter % 900 == 0) ++ if ((this.autosavePeriod > 0) && ((this.tickCounter % this.autosavePeriod) == 0)) // CraftBukkit + { ++ SpigotTimings.worldSaveTimer.startTiming(); // Spigot + this.theProfiler.startSection("save"); + this.serverConfigManager.saveAllPlayerData(); +- this.saveAllWorlds(true); ++ try ++ { ++ this.saveAllWorlds(true); ++ } ++ catch (MinecraftException e) ++ { ++ e.printStackTrace(); ++ } + this.theProfiler.endSection(); ++ SpigotTimings.worldSaveTimer.stopTiming(); // Spigot + } + + this.theProfiler.startSection("tallying"); +@@ -575,25 +835,57 @@ + this.theProfiler.endSection(); + this.theProfiler.startSection("snooper"); + +- if (!this.usageSnooper.isSnooperRunning() && this.tickCounter > 100) ++ if (isSnooperEnabled() && !this.usageSnooper.isSnooperRunning() && this.tickCounter > 100) // Spigot + { + this.usageSnooper.startSnooper(); + } + +- if (this.tickCounter % 6000 == 0) ++ if (isSnooperEnabled() && this.tickCounter % 6000 == 0) // Spigot + { + this.usageSnooper.addMemoryStatsToSnooper(); + } + + this.theProfiler.endSection(); + this.theProfiler.endSection(); ++ callingForgeTick = true; // Cauldron start - handle loadOnProviderRequests during forge tick event + FMLCommonHandler.instance().onPostServerTick(); ++ callingForgeTick = false; // Cauldron end ++ SpigotTimings.serverTickTimer.stopTiming(); // Spigot ++ org.spigotmc.CustomTimingsHandler.tick(); // Spigot + } + + public void updateTimeLightAndEntities() + { + this.theProfiler.startSection("levels"); ++ SpigotTimings.schedulerTimer.startTiming(); // Spigot ++ // CraftBukkit start ++ this.server.getScheduler().mainThreadHeartbeat(this.tickCounter); ++ SpigotTimings.schedulerTimer.stopTiming(); // Spigot ++ ++ // Run tasks that are waiting on processing ++ SpigotTimings.processQueueTimer.startTiming(); // Spigot ++ while (!processQueue.isEmpty()) ++ { ++ processQueue.remove().run(); ++ } ++ SpigotTimings.processQueueTimer.stopTiming(); // Spigot ++ ++ SpigotTimings.chunkIOTickTimer.startTiming(); // Spigot + net.minecraftforge.common.chunkio.ChunkIOExecutor.tick(); ++ SpigotTimings.chunkIOTickTimer.stopTiming(); // Spigot ++ ++ SpigotTimings.timeUpdateTimer.startTiming(); // Spigot ++ // Send time updates to everyone, it will get the right time from the world the player is in. ++ if (this.tickCounter % 20 == 0) ++ { ++ for (int i = 0; i < this.getConfigurationManager().playerEntityList.size(); ++i) ++ { ++ EntityPlayerMP entityplayermp = (EntityPlayerMP) this.getConfigurationManager().playerEntityList.get(i); ++ entityplayermp.playerNetServerHandler.sendPacket(new S03PacketTimeUpdate(entityplayermp.worldObj.getTotalWorldTime(), entityplayermp.getPlayerTime(), entityplayermp.worldObj.getGameRules().getGameRuleBooleanValue("doDaylightCycle"))); // Add support for per player time ++ } ++ } ++ SpigotTimings.timeUpdateTimer.stopTiming(); // Spigot ++ + int i; + + Integer[] ids = DimensionManager.getIDs(this.tickCounter % 200 == 0); +@@ -602,19 +894,21 @@ + int id = ids[x]; + long j = System.nanoTime(); + +- if (id == 0 || this.getAllowNether()) +- { ++ // CraftBukkit start ++ //if (id == 0 || this.getAllowNether()) ++ //{ + WorldServer worldserver = DimensionManager.getWorld(id); + this.theProfiler.startSection(worldserver.getWorldInfo().getWorldName()); + this.theProfiler.startSection("pools"); + this.theProfiler.endSection(); +- ++ /* Drop global time updates + if (this.tickCounter % 20 == 0) + { + this.theProfiler.startSection("timeSync"); + this.serverConfigManager.sendPacketToAllPlayersInDimension(new S03PacketTimeUpdate(worldserver.getTotalWorldTime(), worldserver.getWorldTime(), worldserver.getGameRules().getGameRuleBooleanValue("doDaylightCycle")), worldserver.provider.dimensionId); + this.theProfiler.endSection(); + } ++ // CraftBukkit end */ + + this.theProfiler.startSection("tick"); + FMLCommonHandler.instance().onPreWorldTick(worldserver); +@@ -622,22 +916,46 @@ + + try + { ++ worldserver.timings.doTick.startTiming(); // Spigot + worldserver.tick(); ++ worldserver.timings.doTick.stopTiming(); // Spigot + } + catch (Throwable throwable1) + { +- crashreport = CrashReport.makeCrashReport(throwable1, "Exception ticking world"); ++ // Spigot Start ++ try ++ { ++ crashreport = CrashReport.makeCrashReport(throwable1, "Exception ticking world"); ++ } ++ catch (Throwable t) ++ { ++ throw new RuntimeException("Error generating crash report", t); ++ } ++ ++ // Spigot End + worldserver.addWorldInfoToCrashReport(crashreport); + throw new ReportedException(crashreport); + } + + try + { ++ worldserver.timings.tickEntities.startTiming(); // Spigot + worldserver.updateEntities(); ++ worldserver.timings.tickEntities.stopTiming(); // Spigot + } + catch (Throwable throwable) + { +- crashreport = CrashReport.makeCrashReport(throwable, "Exception ticking world entities"); ++ // Spigot Start ++ try ++ { ++ crashreport = CrashReport.makeCrashReport(throwable, "Exception ticking world entities"); ++ } ++ catch (Throwable t) ++ { ++ throw new RuntimeException("Error generating crash report", t); ++ } ++ ++ // Spigot End + worldserver.addWorldInfoToCrashReport(crashreport); + throw new ReportedException(crashreport); + } +@@ -645,10 +963,12 @@ + FMLCommonHandler.instance().onPostWorldTick(worldserver); + this.theProfiler.endSection(); + this.theProfiler.startSection("tracker"); ++ worldserver.timings.tracker.startTiming(); // Spigot + worldserver.getEntityTracker().updateTrackedEntities(); ++ worldserver.timings.tracker.stopTiming(); // Spigot + this.theProfiler.endSection(); + this.theProfiler.endSection(); +- } ++ // } // CraftBukkit + + worldTickTimes.get(id)[this.tickCounter % 100] = System.nanoTime() - j; + } +@@ -656,15 +976,21 @@ + this.theProfiler.endStartSection("dim_unloading"); + DimensionManager.unloadWorlds(worldTickTimes); + this.theProfiler.endStartSection("connection"); ++ SpigotTimings.connectionTimer.startTiming(); // Spigot + this.func_147137_ag().networkTick(); ++ SpigotTimings.connectionTimer.stopTiming(); // Spigot + this.theProfiler.endStartSection("players"); ++ SpigotTimings.playerListTimer.startTiming(); // Spigot + this.serverConfigManager.sendPlayerInfoToAllPlayers(); ++ SpigotTimings.playerListTimer.stopTiming(); // Spigot + this.theProfiler.endStartSection("tickables"); + ++ SpigotTimings.tickablesTimer.startTiming(); // Spigot + for (i = 0; i < this.tickables.size(); ++i) + { + ((IUpdatePlayerListBox)this.tickables.get(i)).update(); + } ++ SpigotTimings.tickablesTimer.stopTiming(); // Spigot + + this.theProfiler.endSection(); + } +@@ -699,6 +1025,13 @@ + + public WorldServer worldServerForDimension(int p_71218_1_) + { ++ // Cauldron start - this is required for MystCraft agebooks to teleport correctly ++ // verify the nether or the end is allowed, and if not return overworld ++ if ((p_71218_1_ == -1 && !this.getAllowNether()) || (p_71218_1_ == 1 && !this.server.getAllowEnd())) ++ { ++ return DimensionManager.getWorld(0); ++ } ++ // Cauldron end + WorldServer ret = DimensionManager.getWorld(p_71218_1_); + if (ret == null) + { +@@ -784,13 +1117,14 @@ + + public List getPossibleCompletions(ICommandSender p_71248_1_, String p_71248_2_) + { +- ArrayList arraylist = new ArrayList(); ++ // Cauldron start - add mod commands to list then pass to bukkit ++ java.util.HashSet arraylist = new java.util.HashSet(); // use a set here to avoid duplicates + + if (p_71248_2_.startsWith("/")) + { +- p_71248_2_ = p_71248_2_.substring(1); +- boolean flag = !p_71248_2_.contains(" "); +- List list = this.commandManager.getPossibleCommands(p_71248_1_, p_71248_2_); ++ String char1 = p_71248_2_.substring(1); // rename var to avoid removing slash from passed message ++ boolean flag = !char1.contains(" "); ++ List list = this.commandManager.getPossibleCommands(p_71248_1_, char1); + + if (list != null) + { +@@ -798,40 +1132,25 @@ + + while (iterator.hasNext()) + { +- String s3 = (String)iterator.next(); ++ String command = (String)iterator.next(); + + if (flag) + { +- arraylist.add("/" + s3); ++ arraylist.add("/" + command); + } + else + { +- arraylist.add(s3); ++ arraylist.add(command); + } + } + } +- +- return arraylist; + } +- else +- { +- String[] astring = p_71248_2_.split(" ", -1); +- String s1 = astring[astring.length - 1]; +- String[] astring1 = this.serverConfigManager.getAllUsernames(); +- int i = astring1.length; + +- for (int j = 0; j < i; ++j) +- { +- String s2 = astring1[j]; +- +- if (CommandBase.doesStringStartWith(s1, s2)) +- { +- arraylist.add(s2); +- } +- } +- +- return arraylist; +- } ++ arraylist.addAll(this.server.tabComplete(p_71248_1_, p_71248_2_)); // add craftbukkit commands ++ ArrayList completions = new ArrayList(arraylist); ++ Collections.sort(completions); // sort the final list ++ return completions; ++ // Cauldron end + } + + public static MinecraftServer getServer() +@@ -1034,7 +1353,7 @@ + + public boolean isServerInOnlineMode() + { +- return this.onlineMode; ++ return this.server.getOnlineMode(); // CraftBukkit + } + + public void setOnlineMode(boolean p_71229_1_) +@@ -1124,7 +1443,7 @@ + + public NetworkSystem func_147137_ag() + { +- return this.field_147144_o; ++ return (this.field_147144_o) == null ? this.field_147144_o = new NetworkSystem(this) : this.field_147144_o; // Spigot + } + + @SideOnly(Side.CLIENT) +@@ -1259,8 +1578,11 @@ + { + Bootstrap.func_151354_b(); + ++ OptionSet options = loadOptions(p_main_0_); ++ + try + { ++ /* CraftBukkit start - Replace everything + boolean flag = true; + String s = null; + String s1 = "."; +@@ -1356,16 +1678,34 @@ + { + dedicatedserver.setGuiEnabled(); + } ++ // */ ++ // CraftBukkit end ++ if (CauldronUtils.deobfuscatedEnvironment()) useJline = false; // Cauldron ++ DedicatedServer dedicatedserver = new DedicatedServer(options); + +- dedicatedserver.startServerThread(); +- Runtime.getRuntime().addShutdownHook(new Thread("Server Shutdown Thread") ++ if (options.has("port")) + { +- private static final String __OBFID = "CL_00001806"; +- public void run() ++ int port = (Integer) options.valueOf("port"); ++ ++ if (port > 0) + { +- dedicatedserver.stopServer(); ++ dedicatedserver.setServerPort(port); + } +- }); ++ } ++ ++ if (options.has("universe")) ++ { ++ dedicatedserver.anvilFile = (File) options.valueOf("universe"); ++ } ++ ++ if (options.has("world")) ++ { ++ dedicatedserver.setFolderName((String) options.valueOf("world")); ++ } ++ ++ dedicatedserver.primaryThread.start(); ++ // Runtime.getRuntime().addShutdownHook(new ThreadShutdown("Server Shutdown Thread", dedicatedserver)); ++ // CraftBukkit end + } + catch (Exception exception) + { +@@ -1400,15 +1740,70 @@ + @SideOnly(Side.SERVER) + public String getPlugins() + { +- return ""; ++ // CraftBukkit start - Whole method ++ StringBuilder result = new StringBuilder(); ++ org.bukkit.plugin.Plugin[] plugins = server.getPluginManager().getPlugins(); ++ result.append(server.getName()); ++ result.append(" on Bukkit "); ++ result.append(server.getBukkitVersion()); ++ ++ if (plugins.length > 0 && this.server.getQueryPlugins()) ++ { ++ result.append(": "); ++ ++ for (int i = 0; i < plugins.length; i++) ++ { ++ if (i > 0) ++ { ++ result.append("; "); ++ } ++ ++ result.append(plugins[i].getDescription().getName()); ++ result.append(" "); ++ result.append(plugins[i].getDescription().getVersion().replaceAll(";", ",")); ++ } ++ } ++ ++ return result.toString(); ++ // CraftBukkit end + } + + @SideOnly(Side.SERVER) +- public String handleRConCommand(String p_71252_1_) ++ public String handleRConCommand(final String par1Str) + { +- RConConsoleSource.instance.resetLog(); +- this.commandManager.executeCommand(RConConsoleSource.instance, p_71252_1_); +- return RConConsoleSource.instance.getLogContents(); ++ Waitable waitable = new Waitable() ++ { ++ @Override ++ protected String evaluate() ++ { ++ RConConsoleSource.instance.resetLog(); ++ // Event changes start ++ RemoteServerCommandEvent event = new RemoteServerCommandEvent(MinecraftServer.this.remoteConsole, par1Str); ++ MinecraftServer.this.server.getPluginManager().callEvent(event); ++ // Event changes end ++ ServerCommand servercommand = new ServerCommand(event.getCommand(), RConConsoleSource.instance); ++ MinecraftServer.this.server.dispatchServerCommand(MinecraftServer.this.remoteConsole, servercommand); // CraftBukkit ++ // this.n.a(RemoteControlCommandListener.instance, s); ++ return RConConsoleSource.instance.getLogContents(); ++ } ++ }; ++ processQueue.add(waitable); ++ ++ try ++ { ++ return waitable.get(); ++ } ++ catch (java.util.concurrent.ExecutionException e) ++ { ++ throw new RuntimeException("Exception processing rcon command " + par1Str, e.getCause()); ++ } ++ catch (InterruptedException e) ++ { ++ Thread.currentThread().interrupt(); // Maintain interrupted state ++ throw new RuntimeException("Interrupted processing rcon command " + par1Str, e); ++ } ++ ++ // CraftBukkit end + } + + @SideOnly(Side.SERVER) +@@ -1455,9 +1850,213 @@ + return this.serverStopped; + } + ++ public static OptionSet loadOptions(String[] args) { ++ OptionParser parser = new OptionParser() { ++ { ++ acceptsAll(Arrays.asList("?", "help"), "Show the help"); ++ ++ acceptsAll(Arrays.asList("c", "config"), "Properties file to use") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("server.properties")) ++ .describedAs("Properties file"); ++ ++ acceptsAll(Arrays.asList("P", "plugins"), "Plugin directory to use") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("plugins")) ++ .describedAs("Plugin directory"); ++ ++ acceptsAll(Arrays.asList("h", "host", "server-ip"), "Host to listen on") ++ .withRequiredArg() ++ .ofType(String.class) ++ .describedAs("Hostname or IP"); ++ ++ acceptsAll(Arrays.asList("W", "world-dir", "universe", "world-container"), "World container") ++ .withRequiredArg() ++ .ofType(File.class) ++ .describedAs("Directory containing worlds"); ++ ++ acceptsAll(Arrays.asList("w", "world", "level-name"), "World name") ++ .withRequiredArg() ++ .ofType(String.class) ++ .describedAs("World name"); ++ ++ acceptsAll(Arrays.asList("p", "port", "server-port"), "Port to listen on") ++ .withRequiredArg() ++ .ofType(Integer.class) ++ .describedAs("Port"); ++ ++ acceptsAll(Arrays.asList("o", "online-mode"), "Whether to use online authentication") ++ .withRequiredArg() ++ .ofType(Boolean.class) ++ .describedAs("Authentication"); ++ ++ acceptsAll(Arrays.asList("s", "size", "max-players"), "Maximum amount of players") ++ .withRequiredArg() ++ .ofType(Integer.class) ++ .describedAs("Server size"); ++ ++ acceptsAll(Arrays.asList("d", "date-format"), "Format of the date to display in the console (for log entries)") ++ .withRequiredArg() ++ .ofType(SimpleDateFormat.class) ++ .describedAs("Log date format"); ++ ++ acceptsAll(Arrays.asList("log-pattern"), "Specfies the log filename pattern") ++ .withRequiredArg() ++ .ofType(String.class) ++ .defaultsTo("server.log") ++ .describedAs("Log filename"); ++ ++ acceptsAll(Arrays.asList("log-limit"), "Limits the maximum size of the log file (0 = unlimited)") ++ .withRequiredArg() ++ .ofType(Integer.class) ++ .defaultsTo(0) ++ .describedAs("Max log size"); ++ ++ acceptsAll(Arrays.asList("log-count"), "Specified how many log files to cycle through") ++ .withRequiredArg() ++ .ofType(Integer.class) ++ .defaultsTo(1) ++ .describedAs("Log count"); ++ ++ acceptsAll(Arrays.asList("log-append"), "Whether to append to the log file") ++ .withRequiredArg() ++ .ofType(Boolean.class) ++ .defaultsTo(true) ++ .describedAs("Log append"); ++ ++ acceptsAll(Arrays.asList("log-strip-color"), "Strips color codes from log file"); ++ ++ acceptsAll(Arrays.asList("b", "bukkit-settings"), "File for bukkit settings") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("bukkit.yml")) ++ .describedAs("Yml file"); ++ ++ acceptsAll(Arrays.asList("C", "commands-settings"), "File for command settings") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("commands.yml")) ++ .describedAs("Yml file"); ++ ++ acceptsAll(Arrays.asList("nojline"), "Disables jline and emulates the vanilla console"); ++ ++ acceptsAll(Arrays.asList("noconsole"), "Disables the console"); ++ ++ acceptsAll(Arrays.asList("v", "version"), "Show the CraftBukkit Version"); ++ ++ acceptsAll(Arrays.asList("demo"), "Demo mode"); ++ } ++ }; ++ ++ OptionSet options = null; ++ ++ try { ++ options = parser.parse(args); ++ } catch (joptsimple.OptionException ex) { ++ logger.log(org.apache.logging.log4j.Level.ERROR, ex.getLocalizedMessage()); ++ } ++ ++ if ((options == null) || (options.has("?"))) { ++ try { ++ parser.printHelpOn(System.out); ++ } catch (IOException ex) { ++ logger.log(org.apache.logging.log4j.Level.ERROR, ex); ++ } ++ } else { ++ try { ++ // This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals ++ String jline_UnsupportedTerminal = new String(new char[] {'j','l','i','n','e','.','U','n','s','u','p','p','o','r','t','e','d','T','e','r','m','i','n','a','l'}); ++ String jline_terminal = new String(new char[] {'j','l','i','n','e','.','t','e','r','m','i','n','a','l'}); ++ ++ useJline = !(jline_UnsupportedTerminal).equals(System.getProperty(jline_terminal)); ++ ++ if (options.has("nojline")) { ++ System.setProperty("user.language", "en"); ++ useJline = false; ++ } ++ ++ if (!useJline) { ++ // This ensures the terminal literal will always match the jline implementation ++ System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName()); ++ } ++ ++ ++ if (options.has("noconsole")) { ++ useConsole = false; ++ } ++ // Cauldron start - initialize config ++ configFile = (File) options.valueOf("bukkit-settings"); ++ commandFile = (File)options.valueOf("commands-settings"); ++ configuration = YamlConfiguration.loadConfiguration(configFile); ++ configuration.options().copyDefaults(true); ++ configuration.setDefaults(YamlConfiguration.loadConfiguration(MinecraftServer.class.getClassLoader().getResourceAsStream("configurations/bukkit.yml"))); ++ ConfigurationSection legacyAlias = null; ++ if (!configuration.isString("aliases")) { ++ legacyAlias = configuration.getConfigurationSection("aliases"); ++ configuration.set("aliases", "now-in-commands.yml"); ++ } ++ try { ++ configuration.save(configFile); ++ } catch (IOException ex) { ++ logger.log(org.apache.logging.log4j.Level.ERROR, "Could not save " + configFile, ex); ++ } ++ if (commandFile.isFile()) { ++ legacyAlias = null; ++ } ++ commandsConfiguration = YamlConfiguration.loadConfiguration(commandFile); ++ commandsConfiguration.options().copyDefaults(true); ++ commandsConfiguration.setDefaults(YamlConfiguration.loadConfiguration(MinecraftServer.class.getClassLoader().getResourceAsStream("configurations/commands.yml"))); ++ try { ++ commandsConfiguration.save(commandFile); ++ } catch (IOException ex) { ++ logger.log(org.apache.logging.log4j.Level.ERROR, "Could not save " + commandFile, ex); ++ } ++ ++ // Migrate aliases from old file and add previously implicit $1- to pass all arguments ++ if (legacyAlias != null) { ++ ConfigurationSection aliases = commandsConfiguration.createSection("aliases"); ++ for (String key : legacyAlias.getKeys(false)) { ++ ArrayList commands = new ArrayList(); ++ ++ if (legacyAlias.isList(key)) { ++ for (String command : legacyAlias.getStringList(key)) { ++ commands.add(command + " $1-"); ++ } ++ } else { ++ commands.add(legacyAlias.getString(key) + " $1-"); ++ } ++ ++ aliases.set(key, commands); ++ } ++ } ++ ++ try { ++ commandsConfiguration.save(commandFile); ++ } catch (IOException ex) { ++ logger.log(org.apache.logging.log4j.Level.ERROR, "Could not save " + commandFile, ex); ++ } ++ ++ return options; ++ // Cauldron end ++ } catch (Throwable t) { ++ t.printStackTrace(); ++ } ++ } ++ return null; // Cauldron ++ } ++ + @SideOnly(Side.SERVER) + public void setForceGamemode(boolean p_104055_1_) + { + this.isGamemodeForced = p_104055_1_; + } ++ ++ // CraftBukkit start ++ public static Logger getLogger() ++ { ++ return logger; ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/server/dedicated/DedicatedServer.java.patch b/patches/net/minecraft/server/dedicated/DedicatedServer.java.patch new file mode 100644 index 0000000..cd70938 --- /dev/null +++ b/patches/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -0,0 +1,249 @@ +--- ../src-base/minecraft/net/minecraft/server/dedicated/DedicatedServer.java ++++ ../src-work/minecraft/net/minecraft/server/dedicated/DedicatedServer.java +@@ -34,9 +34,19 @@ + import net.minecraft.world.World; + import net.minecraft.world.WorldSettings; + import net.minecraft.world.WorldType; ++import net.minecraft.world.chunk.storage.AnvilSaveConverter; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.io.PrintStream; ++import org.apache.logging.log4j.Level; ++ ++import org.bukkit.craftbukkit.LoggerOutputStream; ++import org.bukkit.craftbukkit.SpigotTimings; // Spigot ++import org.bukkit.event.server.ServerCommandEvent; ++// CraftBukkit end ++ + @SideOnly(Side.SERVER) + public class DedicatedServer extends MinecraftServer implements IServer + { +@@ -44,7 +54,7 @@ + public final List pendingCommandList = Collections.synchronizedList(new ArrayList()); + private RConThreadQuery theRConThreadQuery; + private RConThreadMain theRConThreadMain; +- private PropertyManager settings; ++ public PropertyManager settings; // CraftBukkit - private -> public + private ServerEula field_154332_n; + private boolean canSpawnStructures; + private WorldSettings.GameType gameType; +@@ -52,9 +62,12 @@ + public static boolean allowPlayerLogins = false; + private static final String __OBFID = "CL_00001784"; + +- public DedicatedServer(File p_i1508_1_) ++ // CraftBukkit start - Signature changed ++ public DedicatedServer(joptsimple.OptionSet options) + { +- super(p_i1508_1_, Proxy.NO_PROXY); ++ super(options, Proxy.NO_PROXY); ++ // super(p_i1508_1_, Proxy.NO_PROXY); ++ // CraftBukkit end + Thread thread = new Thread("Server Infinisleeper") + { + private static final String __OBFID = "CL_00001787"; +@@ -82,31 +95,77 @@ + }; + } + +- protected boolean startServer() throws IOException ++ protected boolean startServer() throws java.net.UnknownHostException // CraftBukkit - throws UnknownHostException + { + Thread thread = new Thread("Server console handler") + { + private static final String __OBFID = "CL_00001786"; ++ final DedicatedServer server = DedicatedServer.this; + public void run() + { +- BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(System.in)); +- String s4; ++ // CraftBukkit start ++ if (!useConsole) ++ { ++ return; ++ } ++ // CraftBukkit end + ++ jline.console.ConsoleReader bufferedreader = this.server.reader; // CraftBukkit ++ String s; ++ + try + { +- while (!DedicatedServer.this.isServerStopped() && DedicatedServer.this.isServerRunning() && (s4 = bufferedreader.readLine()) != null) ++ // CraftBukkit start - JLine disabling compatibility ++ while (!this.server.isServerStopped() && this.server.isServerRunning()) + { +- DedicatedServer.this.addPendingCommand(s4, DedicatedServer.this); ++ if (useJline) ++ { ++ s = bufferedreader.readLine(">", null); ++ } ++ else ++ { ++ s = bufferedreader.readLine(); ++ } ++ if (s != null) ++ { ++ this.server.addPendingCommand(s, this.server); ++ } ++ // CraftBukkit end + } ++ + } +- catch (IOException ioexception1) ++ catch (IOException ioexception) + { +- DedicatedServer.field_155771_h.error("Exception handling console input", ioexception1); ++ DedicatedServer.field_155771_h.error("Exception handling console input", ioexception); + } + } + }; + thread.setDaemon(true); + thread.start(); ++ // CraftBukkit start - TODO: handle command-line logging arguments ++ java.util.logging.Logger global = java.util.logging.Logger.getLogger(""); ++ global.setUseParentHandlers(false); ++ ++ for (java.util.logging.Handler handler : global.getHandlers()) ++ { ++ global.removeHandler(handler); ++ } ++ ++ global.addHandler(new org.bukkit.craftbukkit.util.ForwardLogHandler()); ++ final org.apache.logging.log4j.core.Logger logger = ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()); ++ ++ for (org.apache.logging.log4j.core.Appender appender : logger.getAppenders().values()) ++ { ++ if (appender instanceof org.apache.logging.log4j.core.appender.ConsoleAppender) ++ { ++ logger.removeAppender(appender); ++ } ++ } ++ ++ new Thread(new org.bukkit.craftbukkit.util.TerminalConsoleWriterThread(System.out, this.reader)).start(); ++ System.setOut(new PrintStream(new LoggerOutputStream(logger, Level.INFO), true)); ++ System.setErr(new PrintStream(new LoggerOutputStream(logger, Level.WARN), true)); ++ // CraftBukkit end + field_155771_h.info("Starting minecraft server version 1.7.10"); + + if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) +@@ -117,7 +176,7 @@ + FMLCommonHandler.instance().onServerStart(this); + + field_155771_h.info("Loading properties"); +- this.settings = new PropertyManager(new File("server.properties")); ++ this.settings = new PropertyManager(this.options); // CraftBukkit - CLI argument support + this.field_154332_n = new ServerEula(new File("eula.txt")); + + if (!this.field_154332_n.func_154346_a()) +@@ -172,6 +231,16 @@ + this.setServerPort(this.settings.getIntProperty("server-port", 25565)); + } + ++ // Spigot start ++ this.func_152361_a((ServerConfigurationManager) (new DedicatedPlayerList(this))); ++ org.spigotmc.SpigotConfig.init(); ++ org.spigotmc.SpigotConfig.registerCommands(); ++ // Spigot end ++ // Cauldron start ++ this.cauldronConfig.registerCommands(); ++ this.tileEntityConfig.registerCommands(); ++ // Cauldron end ++ + field_155771_h.info("Generating keypair"); + this.setKeyPair(CryptManager.createNewKeyPair()); + field_155771_h.info("Starting Minecraft server on " + (this.getServerHostname().length() == 0 ? "*" : this.getServerHostname()) + ":" + this.getServerPort()); +@@ -180,7 +249,7 @@ + { + this.func_147137_ag().addLanEndpoint(inetaddress, this.getServerPort()); + } +- catch (IOException ioexception) ++ catch (Throwable ioexception) // CraftBukkit - IOException -> Throwable + { + field_155771_h.warn("**** FAILED TO BIND TO PORT!"); + field_155771_h.warn("The exception was: {}", new Object[] {ioexception.toString()}); +@@ -196,10 +265,17 @@ + field_155771_h.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file."); + } + +- if (this.func_152368_aE()) ++ try + { +- this.func_152358_ax().func_152658_c(); ++ if (this.func_152368_aE()) ++ { ++ this.func_152358_ax().func_152658_c(); ++ } + } ++ catch (IOException e) ++ { ++ e.printStackTrace(); ++ } + + if (!PreYggdrasilConverter.func_152714_a(this.settings)) + { +@@ -208,7 +284,8 @@ + else + { + FMLCommonHandler.instance().onServerStarted(); +- this.func_152361_a(new DedicatedPlayerList(this)); ++ // this.func_152361_a(new DedicatedPlayerList(this)); // CraftBukkit - moved up ++ this.anvilConverterForAnvilFile = new AnvilSaveConverter(server.getWorldContainer()); // CraftBukkit - moved from MinecraftServer constructor + long j = System.nanoTime(); + + if (this.getFolderName() == null) +@@ -274,11 +351,30 @@ + this.theRConThreadMain.startThread(); + } + ++ // CraftBukkit start ++ if (this.server.getBukkitSpawnRadius() > -1) ++ { ++ field_155771_h ++ .info("'settings.spawn-radius' in bukkit.yml has been moved to 'spawn-protection' in server.properties. I will move your config for you."); ++ this.settings.serverProperties.remove("spawn-protection"); ++ this.settings.getIntProperty("spawn-protection", this.server.getBukkitSpawnRadius()); ++ this.server.removeBukkitSpawnRadius(); ++ this.settings.saveProperties(); ++ } ++ // CraftBukkit end ++ + return FMLCommonHandler.instance().handleServerStarting(this); + } + } + } + ++ // CraftBukkit start ++ public PropertyManager getPropertyManager() ++ { ++ return this.settings; ++ } ++ // CraftBukkit end ++ + public boolean canStructuresSpawn() + { + return this.canSpawnStructures; +@@ -364,11 +460,19 @@ + + public void executePendingCommands() + { ++ SpigotTimings.serverCommandTimer.startTiming(); // Spigot + while (!this.pendingCommandList.isEmpty()) + { +- ServerCommand servercommand = (ServerCommand)this.pendingCommandList.remove(0); +- this.getCommandManager().executeCommand(servercommand.sender, servercommand.command); ++ ServerCommand servercommand = (ServerCommand) this.pendingCommandList.remove(0); ++ // CraftBukkit start - ServerCommand for preprocessing ++ ServerCommandEvent event = new ServerCommandEvent(this.console, servercommand.command); ++ this.server.getPluginManager().callEvent(event); ++ servercommand = new ServerCommand(event.getCommand(), servercommand.sender); ++ // this.getCommandManager().executeCommand(servercommand.sender, servercommand.command); // Called in dispatchServerCommand ++ this.server.dispatchServerCommand(this.console, servercommand); ++ // CraftBukkit end + } ++ SpigotTimings.serverCommandTimer.stopTiming(); // Spigot + } + + public boolean isDedicatedServer() diff --git a/patches/net/minecraft/server/dedicated/PropertyManager.java.patch b/patches/net/minecraft/server/dedicated/PropertyManager.java.patch new file mode 100644 index 0000000..cf3180e --- /dev/null +++ b/patches/net/minecraft/server/dedicated/PropertyManager.java.patch @@ -0,0 +1,98 @@ +--- ../src-base/minecraft/net/minecraft/server/dedicated/PropertyManager.java ++++ ../src-work/minecraft/net/minecraft/server/dedicated/PropertyManager.java +@@ -10,11 +10,13 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import joptsimple.OptionSet; // CraftBukkit ++ + @SideOnly(Side.SERVER) + public class PropertyManager + { + private static final Logger field_164440_a = LogManager.getLogger(); +- private final Properties serverProperties = new Properties(); ++ public final Properties serverProperties = new Properties(); // CraftBukkit - private -> public + private final File serverPropertiesFile; + private static final String __OBFID = "CL_00001782"; + +@@ -58,6 +60,26 @@ + } + } + ++ // CraftBukkit start ++ private OptionSet options = null; ++ ++ public PropertyManager(final OptionSet options) ++ { ++ this((File) options.valueOf("config")); ++ this.options = options; ++ } ++ ++ private T getOverride(String name, T value) ++ { ++ if ((this.options != null) && (this.options.has(name))) ++ { ++ return (T) this.options.valueOf(name); ++ } ++ ++ return value; ++ } ++ // CraftBukkit end ++ + public void generateNewProperties() + { + field_164440_a.info("Generating new properties file"); +@@ -70,6 +92,13 @@ + + try + { ++ // CraftBukkit start - Don't attempt writing to file if it's read only ++ if (this.serverPropertiesFile.exists() && !this.serverPropertiesFile.canWrite()) ++ { ++ return; ++ } ++ ++ // CraftBukkit end + fileoutputstream = new FileOutputStream(this.serverPropertiesFile); + this.serverProperties.store(fileoutputstream, "Minecraft server properties"); + } +@@ -108,20 +137,20 @@ + this.saveProperties(); + } + +- return this.serverProperties.getProperty(p_73671_1_, p_73671_2_); ++ return this.getOverride(p_73671_1_, this.serverProperties.getProperty(p_73671_1_, p_73671_2_)); // CraftBukkit + } + + public int getIntProperty(String p_73669_1_, int p_73669_2_) + { + try + { +- return Integer.parseInt(this.getStringProperty(p_73669_1_, "" + p_73669_2_)); ++ return this.getOverride(p_73669_1_, Integer.parseInt(this.getStringProperty(p_73669_1_, "" + p_73669_2_))); // CraftBukkit + } + catch (Exception exception) + { + this.serverProperties.setProperty(p_73669_1_, "" + p_73669_2_); + this.saveProperties(); +- return p_73669_2_; ++ return this.getOverride(p_73669_1_, p_73669_2_); // CraftBukkit + } + } + +@@ -129,13 +158,13 @@ + { + try + { +- return Boolean.parseBoolean(this.getStringProperty(p_73670_1_, "" + p_73670_2_)); ++ return this.getOverride(p_73670_1_, Boolean.parseBoolean(this.getStringProperty(p_73670_1_, "" + p_73670_2_))); // CraftBukkit + } + catch (Exception exception) + { + this.serverProperties.setProperty(p_73670_1_, "" + p_73670_2_); + this.saveProperties(); +- return p_73670_2_; ++ return this.getOverride(p_73670_1_, p_73670_2_); // CraftBukkit + } + } + diff --git a/patches/net/minecraft/server/integrated/IntegratedServer.java.patch b/patches/net/minecraft/server/integrated/IntegratedServer.java.patch new file mode 100644 index 0000000..98d30c1 --- /dev/null +++ b/patches/net/minecraft/server/integrated/IntegratedServer.java.patch @@ -0,0 +1,60 @@ +--- ../src-base/minecraft/net/minecraft/server/integrated/IntegratedServer.java ++++ ../src-work/minecraft/net/minecraft/server/integrated/IntegratedServer.java +@@ -13,9 +13,11 @@ + import net.minecraft.crash.CrashReport; + import net.minecraft.profiler.PlayerUsageSnooper; + import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.dedicated.PropertyManager; + import net.minecraft.util.CryptManager; + import net.minecraft.util.HttpUtil; + import net.minecraft.world.EnumDifficulty; ++import net.minecraft.world.MinecraftException; + import net.minecraft.world.WorldManager; + import net.minecraft.world.WorldServer; + import net.minecraft.world.WorldServerMulti; +@@ -78,7 +80,7 @@ + this.initialWorldChunkLoad(); + } + +- protected boolean startServer() throws IOException ++ protected boolean startServer() throws java.net.UnknownHostException // Cauldron + { + logger.info("Starting integrated minecraft server version 1.7.10"); + this.setOnlineMode(true); +@@ -103,7 +105,15 @@ + { + logger.info("Saving and pausing game..."); + this.getConfigurationManager().saveAllPlayerData(); +- this.saveAllWorlds(false); ++ try ++ { ++ this.saveAllWorlds(false); ++ } ++ catch (MinecraftException e) ++ { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } + } + + if (!this.isGamePaused) +@@ -236,7 +246,7 @@ + } + } + +- public void stopServer() ++ public void stopServer() throws MinecraftException // Cauldron + { + super.stopServer(); + +@@ -277,4 +287,10 @@ + { + return 4; + } ++ ++ @Override ++ public PropertyManager getPropertyManager() ++ { ++ return null; ++ } + } diff --git a/patches/net/minecraft/server/management/BanEntry.java.patch b/patches/net/minecraft/server/management/BanEntry.java.patch new file mode 100644 index 0000000..9c6936c --- /dev/null +++ b/patches/net/minecraft/server/management/BanEntry.java.patch @@ -0,0 +1,33 @@ +--- ../src-base/minecraft/net/minecraft/server/management/BanEntry.java ++++ ../src-work/minecraft/net/minecraft/server/management/BanEntry.java +@@ -76,4 +76,30 @@ + p_152641_1_.addProperty("expires", this.banEndDate == null ? "forever" : dateFormat.format(this.banEndDate)); + p_152641_1_.addProperty("reason", this.reason); + } ++ ++ // CraftBukkit start ++ public String getSource() { ++ return this.bannedBy; ++ } ++ ++ public Date getCreated() { ++ return this.banStartDate; ++ } ++ ++ private static Object checkExpiry(Object object, JsonObject jsonobject) { ++ Date expires = null; ++ ++ try { ++ expires = jsonobject.has("expires") ? dateFormat.parse(jsonobject.get("expires").getAsString()) : null; ++ } catch (ParseException ex) { ++ // Guess we don't have a date ++ } ++ ++ if (expires == null || expires.after(new Date())) { ++ return object; ++ } else { ++ return null; ++ } ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/server/management/ItemInWorldManager.java.patch b/patches/net/minecraft/server/management/ItemInWorldManager.java.patch new file mode 100644 index 0000000..42e6655 --- /dev/null +++ b/patches/net/minecraft/server/management/ItemInWorldManager.java.patch @@ -0,0 +1,298 @@ +--- ../src-base/minecraft/net/minecraft/server/management/ItemInWorldManager.java ++++ ../src-work/minecraft/net/minecraft/server/management/ItemInWorldManager.java +@@ -4,6 +4,7 @@ + import net.minecraft.block.material.Material; + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.item.Item; + import net.minecraft.item.ItemStack; + import net.minecraft.item.ItemSword; + import net.minecraft.network.play.server.S23PacketBlockChange; +@@ -13,13 +14,28 @@ + import net.minecraft.world.WorldSettings; + import net.minecraftforge.common.ForgeHooks; + import net.minecraftforge.common.MinecraftForge; +-import cpw.mods.fml.common.eventhandler.Event; + import net.minecraftforge.event.ForgeEventFactory; + import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent; +-import net.minecraftforge.event.entity.player.PlayerInteractEvent; +-import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action; + import net.minecraftforge.event.world.BlockEvent; + ++// CraftBukkit start ++import net.minecraft.init.Blocks; ++ ++import org.bukkit.event.block.BlockBreakEvent; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.event.Event; ++import org.bukkit.event.block.Action; ++import org.bukkit.event.player.PlayerInteractEvent; ++// CraftBukkit end ++// Cauldron start ++import net.minecraft.inventory.ContainerPlayer; ++import net.minecraft.inventory.IInventory; ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++import org.bukkit.event.inventory.InventoryType; ++// Cauldron end ++ + public class ItemInWorldManager + { + /** Forge reach distance */ +@@ -135,15 +151,29 @@ + + public void onBlockClicked(int p_73074_1_, int p_73074_2_, int p_73074_3_, int p_73074_4_) + { ++ // CraftBukkit start ++ org.bukkit.event.player.PlayerInteractEvent cbEvent = CraftEventFactory.callPlayerInteractEvent(this.thisPlayerMP, Action.LEFT_CLICK_BLOCK, p_73074_1_, p_73074_2_, p_73074_3_, p_73074_4_, this.thisPlayerMP.inventory.getCurrentItem()); ++ + if (!this.gameType.isAdventure() || this.thisPlayerMP.isCurrentToolAdventureModeExempt(p_73074_1_, p_73074_2_, p_73074_3_)) + { +- PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(thisPlayerMP, Action.LEFT_CLICK_BLOCK, p_73074_1_, p_73074_2_, p_73074_3_, p_73074_4_, theWorld); +- if (event.isCanceled()) ++ net.minecraftforge.event.entity.player.PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(this.thisPlayerMP, net.minecraftforge.event.entity.player.PlayerInteractEvent.Action.LEFT_CLICK_BLOCK, p_73074_1_, p_73074_2_, p_73074_3_, p_73074_4_, theWorld); // Forge ++ ++ if (cbEvent.isCancelled() || event.isCanceled()) + { +- thisPlayerMP.playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_, p_73074_3_, theWorld)); ++ // Let the client know the block still exists ++ ((EntityPlayerMP) this.thisPlayerMP).playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_, p_73074_3_, this.theWorld)); ++ // Update any tile entity data for this block ++ TileEntity tileentity = this.theWorld.getTileEntity(p_73074_1_, p_73074_2_, p_73074_3_); ++ ++ if (tileentity != null) ++ { ++ this.thisPlayerMP.playerNetServerHandler.sendPacket(tileentity.getDescriptionPacket()); ++ } ++ + return; + } + ++ // CraftBukkit end + if (this.isCreative()) + { + if (!this.theWorld.extinguishFire((EntityPlayer)null, p_73074_1_, p_73074_2_, p_73074_3_, p_73074_4_)) +@@ -157,30 +187,56 @@ + float f = 1.0F; + Block block = this.theWorld.getBlock(p_73074_1_, p_73074_2_, p_73074_3_); + +- +- if (!block.isAir(theWorld, p_73074_1_, p_73074_2_, p_73074_3_)) ++ // CraftBukkit start - Swings at air do *NOT* exist. ++ if (cbEvent.useInteractedBlock() == org.bukkit.event.Event.Result.DENY || event.useBlock == cpw.mods.fml.common.eventhandler.Event.Result.DENY) // Cauldron + { +- if (event.useBlock != Event.Result.DENY) ++ // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. ++ if (block == Blocks.wooden_door) + { +- block.onBlockClicked(theWorld, p_73074_1_, p_73074_2_, p_73074_3_, thisPlayerMP); +- theWorld.extinguishFire(null, p_73074_1_, p_73074_2_, p_73074_3_, p_73074_4_); ++ // For some reason *BOTH* the bottom/top part have to be marked updated. ++ boolean bottom = (this.theWorld.getBlockMetadata(p_73074_1_, p_73074_2_, p_73074_3_) & 8) == 0; ++ ((EntityPlayerMP) this.thisPlayerMP).playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_, p_73074_3_, this.theWorld)); ++ ((EntityPlayerMP) this.thisPlayerMP).playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_ + (bottom ? 1 : -1), p_73074_3_, this.theWorld)); + } +- else ++ else if (block == Blocks.trapdoor) + { +- thisPlayerMP.playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_, p_73074_3_, theWorld)); ++ ((EntityPlayerMP) this.thisPlayerMP).playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_, p_73074_3_, this.theWorld)); + } +- f = block.getPlayerRelativeBlockHardness(thisPlayerMP, thisPlayerMP.worldObj, p_73074_1_, p_73074_2_, p_73074_3_); + } +- +- if (event.useItem == Event.Result.DENY) ++ else if (!block.isAir(theWorld, p_73074_1_, p_73074_2_, p_73074_3_)) + { +- if (f >= 1.0f) ++ block.onBlockClicked(this.theWorld, p_73074_1_, p_73074_2_, p_73074_3_, this.thisPlayerMP); ++ f = block.getPlayerRelativeBlockHardness(this.thisPlayerMP, this.thisPlayerMP.worldObj, p_73074_1_, p_73074_2_, p_73074_3_); ++ // Allow fire punching to be blocked ++ this.theWorld.extinguishFire((EntityPlayer) null, p_73074_1_, p_73074_2_, p_73074_3_, p_73074_4_); ++ } ++ if (cbEvent.useItemInHand() == org.bukkit.event.Event.Result.DENY || event.useItem == cpw.mods.fml.common.eventhandler.Event.Result.DENY) // Forge ++ { ++ // If we 'insta destroyed' then the client needs to be informed. ++ if (f > 1.0f) + { +- thisPlayerMP.playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_, p_73074_3_, theWorld)); ++ ((EntityPlayerMP) this.thisPlayerMP).playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_, p_73074_3_, this.theWorld)); + } ++ + return; + } + ++ org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.thisPlayerMP, p_73074_1_, p_73074_2_, p_73074_3_, this.thisPlayerMP.inventory.getCurrentItem(), f >= 1.0f); ++ ++ if (blockEvent.isCancelled()) ++ { ++ // Let the client know the block still exists ++ ((EntityPlayerMP) this.thisPlayerMP).playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73074_1_, p_73074_2_, p_73074_3_, this.theWorld)); ++ return; ++ } ++ ++ if (blockEvent.getInstaBreak()) ++ { ++ f = 2.0f; ++ } ++ ++ // CraftBukkit end ++ + if (!block.isAir(theWorld, p_73074_1_, p_73074_2_, p_73074_3_) && f >= 1.0F) + { + this.tryHarvestBlock(p_73074_1_, p_73074_2_, p_73074_3_); +@@ -269,6 +325,12 @@ + return false; + } + Block block = this.theWorld.getBlock(p_73084_1_, p_73084_2_, p_73084_3_); ++ ++ if (block == Blocks.air) ++ { ++ return false; // CraftBukkit - A plugin set block to air without cancelling ++ } ++ + int l = this.theWorld.getBlockMetadata(p_73084_1_, p_73084_2_, p_73084_3_); + this.theWorld.playAuxSFXAtEntity(this.thisPlayerMP, 2001, p_73084_1_, p_73084_2_, p_73084_3_, Block.getIdFromBlock(block) + (this.theWorld.getBlockMetadata(p_73084_1_, p_73084_2_, p_73084_3_) << 12)); + boolean flag = false; +@@ -350,57 +412,104 @@ + + public boolean activateBlockOrUseItem(EntityPlayer p_73078_1_, World p_73078_2_, ItemStack p_73078_3_, int p_73078_4_, int p_73078_5_, int p_73078_6_, int p_73078_7_, float p_73078_8_, float p_73078_9_, float p_73078_10_) + { +- PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(p_73078_1_, Action.RIGHT_CLICK_BLOCK, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_7_, p_73078_2_); +- if (event.isCanceled()) +- { +- thisPlayerMP.playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73078_4_, p_73078_5_, p_73078_6_, theWorld)); +- return false; +- } +- +- if (p_73078_3_ != null && p_73078_3_.getItem().onItemUseFirst(p_73078_3_, p_73078_1_, p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_7_, p_73078_8_, p_73078_9_, p_73078_10_)) +- { +- if (p_73078_3_.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(thisPlayerMP, p_73078_3_); +- return true; +- } +- ++ // CraftBukkit start - Interact + Block block = p_73078_2_.getBlock(p_73078_4_, p_73078_5_, p_73078_6_); +- boolean isAir = block.isAir(p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_); +- boolean useBlock = !p_73078_1_.isSneaking() || p_73078_1_.getHeldItem() == null; +- if (!useBlock) useBlock = p_73078_1_.getHeldItem().getItem().doesSneakBypassUse(p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_1_); ++ boolean isAir = block.isAir(p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_); // Cauldron + boolean result = false; + +- if (useBlock) ++ if (!isAir) + { +- if (event.useBlock != Event.Result.DENY) ++ org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(p_73078_1_, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_7_, p_73078_3_); ++ net.minecraftforge.event.entity.player.PlayerInteractEvent forgeEvent = ForgeEventFactory.onPlayerInteract(p_73078_1_, net.minecraftforge.event.entity.player.PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_7_, p_73078_2_); ++ // Cauldron start ++ // if forge event is explicitly cancelled, return ++ if (forgeEvent.isCanceled()) + { ++ thisPlayerMP.playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73078_4_, p_73078_5_, p_73078_6_, theWorld)); ++ return false; ++ } ++ // if we have no explicit deny, check if item can be used ++ if (event.useItemInHand() != org.bukkit.event.Event.Result.DENY && forgeEvent.useItem != cpw.mods.fml.common.eventhandler.Event.Result.DENY) ++ { ++ Item item = (p_73078_3_ != null ? p_73078_3_.getItem() : null); ++ // try to use an item in hand before activating a block. Used for items such as IC2's wrench. ++ if (item != null && item.onItemUseFirst(p_73078_3_, p_73078_1_, p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_7_, p_73078_8_, p_73078_9_, p_73078_10_)) ++ { ++ if (p_73078_3_.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(thisPlayerMP, p_73078_3_); ++ return true; ++ } ++ } ++ // Cauldron end ++ if (event.useInteractedBlock() == org.bukkit.event.Event.Result.DENY || forgeEvent.useBlock == cpw.mods.fml.common.eventhandler.Event.Result.DENY) ++ { ++ // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. ++ if (block == Blocks.wooden_door) ++ { ++ boolean bottom = (p_73078_2_.getBlockMetadata(p_73078_4_, p_73078_5_, p_73078_6_) & 8) == 0; ++ ((EntityPlayerMP) p_73078_1_).playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73078_4_, p_73078_5_ + (bottom ? 1 : -1), p_73078_6_, p_73078_2_)); ++ } ++ ++ result = (event.useItemInHand() != org.bukkit.event.Event.Result.ALLOW); ++ } ++ else if (!p_73078_1_.isSneaking() || p_73078_3_ == null || p_73078_1_.getHeldItem().getItem().doesSneakBypassUse(p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_1_)) ++ { + result = block.onBlockActivated(p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_1_, p_73078_7_, p_73078_8_, p_73078_9_, p_73078_10_); ++ // Cauldron start - if bukkitView is null, create one. Required for Ender Chests since they do not use NetworkRegistry.openRemoteGUI ++ if (thisPlayerMP != null && !(thisPlayerMP.openContainer instanceof ContainerPlayer)) ++ { ++ if (thisPlayerMP.openContainer.getBukkitView() == null) ++ { ++ TileEntity te = thisPlayerMP.worldObj.getTileEntity(p_73078_4_, p_73078_5_, p_73078_6_); ++ if (te != null && te instanceof IInventory) ++ { ++ IInventory teInv = (IInventory)te; ++ CraftInventory inventory = new CraftInventory(teInv); ++ thisPlayerMP.openContainer.bukkitView = new CraftInventoryView(thisPlayerMP.getBukkitEntity(), inventory, thisPlayerMP.openContainer); ++ } ++ else ++ { ++ thisPlayerMP.openContainer.bukkitView = new CraftInventoryView(thisPlayerMP.getBukkitEntity(), MinecraftServer.getServer().server.createInventory(thisPlayerMP.getBukkitEntity(), InventoryType.CHEST), thisPlayerMP.openContainer); ++ } ++ ++ thisPlayerMP.openContainer = CraftEventFactory.callInventoryOpenEvent(thisPlayerMP, thisPlayerMP.openContainer, false); ++ if (thisPlayerMP.openContainer == null) ++ { ++ thisPlayerMP.openContainer = thisPlayerMP.inventoryContainer; ++ return false; ++ } ++ } ++ } ++ // Cauldron end + } +- else ++ ++ if (p_73078_3_ != null && !result) + { +- thisPlayerMP.playerNetServerHandler.sendPacket(new S23PacketBlockChange(p_73078_4_, p_73078_5_, p_73078_6_, theWorld)); +- result = event.useItem != Event.Result.ALLOW; ++ int meta = p_73078_3_.getItemDamage(); ++ int size = p_73078_3_.stackSize; ++ result = p_73078_3_.tryPlaceItemIntoWorld(p_73078_1_, p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_7_, p_73078_8_, p_73078_9_, p_73078_10_); ++ ++ // The item count should not decrement in Creative mode. ++ if (this.isCreative()) ++ { ++ p_73078_3_.setItemDamage(meta); ++ p_73078_3_.stackSize = size; ++ } ++ ++ if (p_73078_3_.stackSize <= 0) ++ { ++ ForgeEventFactory.onPlayerDestroyItem(this.thisPlayerMP, p_73078_3_); ++ } + } +- } + +- if (p_73078_3_ != null && !result && event.useItem != Event.Result.DENY) +- { +- int meta = p_73078_3_.getItemDamage(); +- int size = p_73078_3_.stackSize; +- result = p_73078_3_.tryPlaceItemIntoWorld(p_73078_1_, p_73078_2_, p_73078_4_, p_73078_5_, p_73078_6_, p_73078_7_, p_73078_8_, p_73078_9_, p_73078_10_); +- if (isCreative()) ++ // If we have 'true' and no explicit deny *or* an explicit allow -- run the item part of the hook ++ if (p_73078_3_ != null && ((!result && event.useItemInHand() != org.bukkit.event.Event.Result.DENY) || event.useItemInHand() == org.bukkit.event.Event.Result.ALLOW)) + { +- p_73078_3_.setItemDamage(meta); +- p_73078_3_.stackSize = size; ++ this.tryUseItem(p_73078_1_, p_73078_2_, p_73078_3_); + } +- if (p_73078_3_.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(thisPlayerMP, p_73078_3_); + } + +- /* Re-enable if this causes bukkit incompatibility, or re-write client side to only send a single packet per right click. +- if (par3ItemStack != null && ((!result && event.useItem != Event.Result.DENY) || event.useItem == Event.Result.ALLOW)) +- { +- this.tryUseItem(thisPlayerMP, par2World, par3ItemStack); +- }*/ + return result; ++ // CraftBukkit end + } + + public void setWorld(WorldServer p_73080_1_) diff --git a/patches/net/minecraft/server/management/PlayerManager.java.patch b/patches/net/minecraft/server/management/PlayerManager.java.patch new file mode 100644 index 0000000..5aee6ec --- /dev/null +++ b/patches/net/minecraft/server/management/PlayerManager.java.patch @@ -0,0 +1,132 @@ +--- ../src-base/minecraft/net/minecraft/server/management/PlayerManager.java ++++ ../src-work/minecraft/net/minecraft/server/management/PlayerManager.java +@@ -18,17 +18,23 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++//CraftBukkit start ++import java.util.Collections; ++import java.util.Queue; ++// CraftBukkit end ++ + public class PlayerManager + { + private static final Logger field_152627_a = LogManager.getLogger(); + private final WorldServer theWorldServer; + private final List players = new ArrayList(); + private final LongHashMap playerInstances = new LongHashMap(); +- private final List chunkWatcherWithPlayers = new ArrayList(); +- private final List playerInstanceList = new ArrayList(); ++ private final Queue chunkWatcherWithPlayers = new java.util.concurrent.ConcurrentLinkedQueue(); // CraftBukkit ArrayList -> ConcurrentLinkedQueue ++ private final Queue playerInstanceList = new java.util.concurrent.ConcurrentLinkedQueue(); // CraftBukkit ArrayList -> ConcurrentLinkedQueue + private int playerViewRadius; + private long previousTotalWorldTime; + private final int[][] xzDirectionsConst = new int[][] {{1, 0}, {0, 1}, { -1, 0}, {0, -1}}; ++ private boolean wasNotEmpty; // CraftBukkit + private static final String __OBFID = "CL_00001434"; + + public PlayerManager(WorldServer p_i1176_1_) +@@ -37,6 +43,14 @@ + this.func_152622_a(p_i1176_1_.func_73046_m().getConfigurationManager().getViewDistance()); + } + ++ // Cauldron start - vanilla compatibility ++ public PlayerManager(WorldServer p_i1176_1_, int viewDistance /* Spigot */) ++ { ++ this.theWorldServer = p_i1176_1_; ++ this.func_152622_a(viewDistance); // Spigot ++ } ++ // Cauldron end ++ + public WorldServer getWorldServer() + { + return this.theWorldServer; +@@ -51,34 +65,53 @@ + if (i - this.previousTotalWorldTime > 8000L) + { + this.previousTotalWorldTime = i; ++ // CraftBukkit start - Use iterator ++ java.util.Iterator iterator = this.playerInstanceList.iterator(); + +- for (j = 0; j < this.playerInstanceList.size(); ++j) ++ while (iterator.hasNext()) + { +- playerinstance = (PlayerManager.PlayerInstance)this.playerInstanceList.get(j); ++ playerinstance = (PlayerManager.PlayerInstance)iterator.next(); + playerinstance.sendChunkUpdate(); + playerinstance.processChunk(); + } + } + else + { +- for (j = 0; j < this.chunkWatcherWithPlayers.size(); ++j) ++ java.util.Iterator iterator = this.chunkWatcherWithPlayers.iterator(); ++ ++ while (iterator.hasNext()) + { +- playerinstance = (PlayerManager.PlayerInstance)this.chunkWatcherWithPlayers.get(j); ++ playerinstance = (PlayerManager.PlayerInstance)iterator.next(); + playerinstance.sendChunkUpdate(); ++ iterator.remove(); ++ // CraftBukkit end + } + } + +- this.chunkWatcherWithPlayers.clear(); ++ // this.chunkWatcherWithPlayers.clear(); // CraftBukkit - Removals are already covered + + if (this.players.isEmpty()) + { ++ if (!wasNotEmpty) ++ { ++ return; // CraftBukkit - Only do unload when we go from non-empty to empty ++ } ++ + WorldProvider worldprovider = this.theWorldServer.provider; + + if (!worldprovider.canRespawnHere()) + { + this.theWorldServer.theChunkProviderServer.unloadAllChunks(); + } ++ ++ // CraftBukkit start ++ wasNotEmpty = false; + } ++ else ++ { ++ wasNotEmpty = true; ++ } ++ // CraftBukkit end + } + + public boolean func_152621_a(int p_152621_1_, int p_152621_2_) +@@ -102,6 +135,20 @@ + return playerinstance; + } + ++ // CraftBukkit start ++ public final boolean isChunkInUse(int x, int z) ++ { ++ PlayerManager.PlayerInstance pi = getOrCreateChunkWatcher(x, z, false); ++ ++ if (pi != null) ++ { ++ return (pi.playersWatchingChunk.size() > 0); ++ } ++ ++ return false; ++ } ++ // CraftBukkit end ++ + public void markBlockForUpdate(int p_151250_1_, int p_151250_2_, int p_151250_3_) + { + int l = p_151250_1_ >> 4; +@@ -541,7 +588,7 @@ + { + i = this.chunkLocation.chunkXPos * 16; + j = this.chunkLocation.chunkZPos * 16; +- this.sendToAllPlayersWatchingChunk(new S21PacketChunkData(PlayerManager.this.theWorldServer.getChunkFromChunkCoords(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos), false, this.flagsYAreasToUpdate)); ++ this.sendToAllPlayersWatchingChunk(new S21PacketChunkData(PlayerManager.this.theWorldServer.getChunkFromChunkCoords(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos), (this.flagsYAreasToUpdate == 0xFFFF), this.flagsYAreasToUpdate)); // CraftBukkit - send everything (including biome) if all sections flagged + + // Forge: Grabs ALL tile entities is costly on a modded server, only send needed ones + for (k = 0; false && k < 16; ++k) diff --git a/patches/net/minecraft/server/management/ServerConfigurationManager.java.patch b/patches/net/minecraft/server/management/ServerConfigurationManager.java.patch new file mode 100644 index 0000000..abfe0c6 --- /dev/null +++ b/patches/net/minecraft/server/management/ServerConfigurationManager.java.patch @@ -0,0 +1,1052 @@ +--- ../src-base/minecraft/net/minecraft/server/management/ServerConfigurationManager.java ++++ ../src-work/minecraft/net/minecraft/server/management/ServerConfigurationManager.java +@@ -6,6 +6,8 @@ + import com.mojang.authlib.GameProfile; + + import cpw.mods.fml.common.FMLCommonHandler; ++import cpw.mods.fml.common.network.FMLEmbeddedChannel; ++import cpw.mods.fml.common.network.FMLOutboundHandler; + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; + import java.io.File; +@@ -58,15 +60,37 @@ + import net.minecraft.world.Teleporter; + import net.minecraft.world.World; + import net.minecraft.world.WorldProvider; ++import net.minecraft.world.WorldProviderEnd; + import net.minecraft.world.WorldServer; + import net.minecraft.world.WorldSettings; + import net.minecraft.world.demo.DemoWorldManager; + import net.minecraft.world.storage.IPlayerFileData; ++import net.minecraftforge.common.DimensionManager; + import net.minecraftforge.common.chunkio.ChunkIOExecutor; ++import net.minecraftforge.common.network.ForgeMessage; ++import net.minecraftforge.common.network.ForgeNetworkHandler; + + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import net.minecraft.server.network.NetHandlerLoginServer; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.Bukkit; ++import org.bukkit.Location; ++import org.bukkit.TravelAgent; ++import org.bukkit.entity.Player; ++import org.bukkit.event.player.PlayerChangedWorldEvent; ++import org.bukkit.event.player.PlayerPortalEvent; ++import org.bukkit.event.player.PlayerJoinEvent; ++import org.bukkit.event.player.PlayerLoginEvent; ++import org.bukkit.event.player.PlayerQuitEvent; ++import org.bukkit.event.player.PlayerRespawnEvent; ++import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; ++import org.bukkit.util.Vector; ++// CraftBukkit end ++ + public abstract class ServerConfigurationManager + { + public static final File field_152613_a = new File("banned-players.json"); +@@ -82,8 +106,8 @@ + private final UserListOps ops; + private final UserListWhitelist whiteListedPlayers; + private final Map field_148547_k; +- private IPlayerFileData playerNBTManagerObj; +- private boolean whiteListEnforced; ++ public IPlayerFileData playerNBTManagerObj; // CraftBukkit - private -> public ++ public boolean whiteListEnforced; // CraftBukkit - private -> public + protected int maxPlayers; + private int viewDistance; + private WorldSettings.GameType gameType; +@@ -91,8 +115,16 @@ + private int playerPingIndex; + private static final String __OBFID = "CL_00001423"; + ++ // CraftBukkit start ++ private CraftServer cserver; ++ + public ServerConfigurationManager(MinecraftServer p_i1500_1_) + { ++ p_i1500_1_.server = new CraftServer(p_i1500_1_, this); ++ p_i1500_1_.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance(); ++ p_i1500_1_.reader.addCompleter(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter(p_i1500_1_.server)); ++ this.cserver = p_i1500_1_.server; ++ // CraftBukkit end + this.bannedPlayers = new UserListBans(field_152613_a); + this.bannedIPs = new BanList(field_152614_b); + this.ops = new UserListOps(field_152615_c); +@@ -131,12 +163,32 @@ + s1 = p_72355_1_.getSocketAddress().toString(); + } + +- logger.info(p_72355_2_.getCommandSenderName() + "[" + s1 + "] logged in with entity id " + p_72355_2_.getEntityId() + " at (" + p_72355_2_.posX + ", " + p_72355_2_.posY + ", " + p_72355_2_.posZ + ")"); ++ // CraftBukkit - add world to 'logged in' message. ++ logger.info(p_72355_2_.getCommandSenderName() + "[" + s1 + "] logged in with entity id " + p_72355_2_.getEntityId() + " at ([" + p_72355_2_.worldObj.worldInfo.getWorldName() + "] " + p_72355_2_.posX + ", " + p_72355_2_.posY + ", " + p_72355_2_.posZ + ")"); + WorldServer worldserver = this.mcServer.worldServerForDimension(p_72355_2_.dimension); + ChunkCoordinates chunkcoordinates = worldserver.getSpawnPoint(); + this.func_72381_a(p_72355_2_, (EntityPlayerMP)null, worldserver); + p_72355_2_.playerNetServerHandler = nethandlerplayserver; ++ // CraftBukkit start -- Don't send a higher than 60 MaxPlayer size, otherwise the PlayerInfo window won't render correctly. ++ int maxPlayers = this.getMaxPlayers(); ++ ++ if (maxPlayers > 60) ++ { ++ maxPlayers = 60; ++ } ++ // CraftBukkit end ++ ++ // Cauldron start - send DimensionRegisterMessage to client before attempting to login to a Bukkit dimension ++ if (DimensionManager.isBukkitDimension(p_72355_2_.dimension)) ++ { ++ FMLEmbeddedChannel serverChannel = ForgeNetworkHandler.getServerChannel(); ++ serverChannel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.PLAYER); ++ serverChannel.attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(p_72355_2_); ++ serverChannel.writeOutbound(new ForgeMessage.DimensionRegisterMessage(p_72355_2_.dimension, worldserver.getWorld().getEnvironment().getId())); ++ } ++ // Cauldron end + nethandlerplayserver.sendPacket(new S01PacketJoinGame(p_72355_2_.getEntityId(), p_72355_2_.theItemInWorldManager.getGameType(), worldserver.getWorldInfo().isHardcoreModeEnabled(), worldserver.provider.dimensionId, worldserver.difficultySetting, this.getMaxPlayers(), worldserver.getWorldInfo().getTerrainType())); ++ p_72355_2_.getBukkitEntity().sendSupportedChannels(); // CraftBukkit + nethandlerplayserver.sendPacket(new S3FPacketCustomPayload("MC|Brand", this.getServerInstance().getServerModName().getBytes(Charsets.UTF_8))); + nethandlerplayserver.sendPacket(new S05PacketSpawnPosition(chunkcoordinates.posX, chunkcoordinates.posY, chunkcoordinates.posZ)); + nethandlerplayserver.sendPacket(new S39PacketPlayerAbilities(p_72355_2_.capabilities)); +@@ -145,6 +197,7 @@ + p_72355_2_.func_147099_x().func_150884_b(p_72355_2_); + this.func_96456_a((ServerScoreboard)worldserver.getScoreboard(), p_72355_2_); + this.mcServer.func_147132_au(); ++ /* CraftBukkit start - login message is handled in the event + ChatComponentTranslation chatcomponenttranslation; + + if (!p_72355_2_.getCommandSenderName().equalsIgnoreCase(s)) +@@ -158,6 +211,7 @@ + + chatcomponenttranslation.getChatStyle().setColor(EnumChatFormatting.YELLOW); + this.sendChatMsg(chatcomponenttranslation); ++ // CraftBukkit end*/ + this.playerLoggedIn(p_72355_2_); + nethandlerplayserver.setPlayerLocation(p_72355_2_.posX, p_72355_2_.posY, p_72355_2_.posZ, p_72355_2_.rotationYaw, p_72355_2_.rotationPitch); + this.updateTimeAndWeatherForPlayer(p_72355_2_, worldserver); +@@ -192,7 +246,7 @@ + } + } + +- protected void func_96456_a(ServerScoreboard p_96456_1_, EntityPlayerMP p_96456_2_) ++ public void func_96456_a(ServerScoreboard p_96456_1_, EntityPlayerMP p_96456_2_) // CraftBukkit - protected -> public + { + HashSet hashset = new HashSet(); + Iterator iterator = p_96456_1_.getTeams().iterator(); +@@ -225,6 +279,11 @@ + + public void setPlayerManager(WorldServer[] p_72364_1_) + { ++ if (this.playerNBTManagerObj != null) ++ { ++ return; // CraftBukkit ++ } ++ + this.playerNBTManagerObj = p_72364_1_[0].getSaveHandler().getSaveHandler(); + } + +@@ -248,7 +307,7 @@ + + public NBTTagCompound readPlayerDataFromFile(EntityPlayerMP p_72380_1_) + { +- NBTTagCompound nbttagcompound = this.mcServer.worldServers[0].getWorldInfo().getPlayerNBTTagCompound(); ++ NBTTagCompound nbttagcompound = this.mcServer.worlds.get(0).getWorldInfo().getPlayerNBTTagCompound(); + NBTTagCompound nbttagcompound1; + + if (p_72380_1_.getCommandSenderName().equals(this.mcServer.getServerOwner()) && nbttagcompound != null) +@@ -281,18 +340,61 @@ + + public void playerLoggedIn(EntityPlayerMP p_72377_1_) + { +- this.sendPacketToAllPlayers(new S38PacketPlayerListItem(p_72377_1_.getCommandSenderName(), true, 1000)); ++ cserver.detectListNameConflict(p_72377_1_); // CraftBukkit ++ // this.sendPacketToAllPlayers(new S38PacketPlayerListItem(p_72377_1_.getCommandSenderName(), true, 1000)); // CraftBukkit - replaced with loop below + this.playerEntityList.add(p_72377_1_); + WorldServer worldserver = this.mcServer.worldServerForDimension(p_72377_1_.dimension); ++ // CraftBukkit start ++ PlayerJoinEvent playerJoinEvent = new PlayerJoinEvent(this.cserver.getPlayer(p_72377_1_), "\u00A7e" + p_72377_1_.getCommandSenderName() ++ + " joined the game."); ++ this.cserver.getPluginManager().callEvent(playerJoinEvent); ++ String joinMessage = playerJoinEvent.getJoinMessage(); ++ ++ if ((joinMessage != null) && (joinMessage.length() > 0)) ++ { ++ for (IChatComponent line : org.bukkit.craftbukkit.util.CraftChatMessage.fromString(joinMessage)) ++ { ++ this.mcServer.getConfigurationManager().sendPacketToAllPlayers(new S02PacketChat(line)); ++ } ++ } ++ ++ this.cserver.onPlayerJoin(playerJoinEvent.getPlayer()); + ChunkIOExecutor.adjustPoolSize(this.getCurrentPlayerCount()); +- worldserver.spawnEntityInWorld(p_72377_1_); +- this.func_72375_a(p_72377_1_, (WorldServer)null); ++ // CraftBukkit end ++ // CraftBukkit start - Only add if the player wasn't moved in the event ++ if (p_72377_1_.worldObj == worldserver && !worldserver.playerEntities.contains(p_72377_1_)) ++ { ++ worldserver.spawnEntityInWorld(p_72377_1_); ++ this.func_72375_a(p_72377_1_, (WorldServer) null); ++ } ++ // CraftBukkit end ++ // CraftBukkit start - sendAll above replaced with this loop ++ S38PacketPlayerListItem packet = new S38PacketPlayerListItem(p_72377_1_.listName, true, 1000); + + for (int i = 0; i < this.playerEntityList.size(); ++i) + { +- EntityPlayerMP entityplayermp1 = (EntityPlayerMP)this.playerEntityList.get(i); +- p_72377_1_.playerNetServerHandler.sendPacket(new S38PacketPlayerListItem(entityplayermp1.getCommandSenderName(), true, entityplayermp1.ping)); ++ EntityPlayerMP entityplayermp1 = (EntityPlayerMP) this.playerEntityList.get(i); ++ ++ if (entityplayermp1.getBukkitEntity().canSee(p_72377_1_.getBukkitEntity())) ++ { ++ entityplayermp1.playerNetServerHandler.sendPacket(packet); ++ } + } ++ // CraftBukkit end ++ for (int i = 0; i < this.playerEntityList.size(); ++i) ++ { ++ EntityPlayerMP entityplayermp1 = (EntityPlayerMP) this.playerEntityList.get(i); ++ ++ // CraftBukkit start ++ if (!p_72377_1_.getBukkitEntity().canSee(entityplayermp1.getBukkitEntity())) ++ { ++ continue; ++ } ++ ++ // .name -> .listName ++ p_72377_1_.playerNetServerHandler.sendPacket(new S38PacketPlayerListItem(entityplayermp1.listName, true, entityplayermp1.ping)); ++ // CraftBukkit end ++ } + } + + public void updatePlayerPertinentChunks(EntityPlayerMP p_72358_1_) +@@ -300,14 +402,33 @@ + p_72358_1_.getServerForPlayer().getPlayerManager().updatePlayerPertinentChunks(p_72358_1_); + } + ++ // Cauldron start - vanilla compatibility + public void playerLoggedOut(EntityPlayerMP p_72367_1_) + { +- FMLCommonHandler.instance().firePlayerLoggedOut(p_72367_1_); ++ disconnect(p_72367_1_); ++ } ++ // Cauldron end ++ ++ public String disconnect(EntityPlayerMP p_72367_1_) // CraftBukkit - return string ++ { + p_72367_1_.triggerAchievement(StatList.leaveGameStat); ++ // Cauldron start - don't show quit messages for players that haven't actually connected ++ PlayerQuitEvent playerQuitEvent = null; ++ if (p_72367_1_.playerNetServerHandler != null) ++ { ++ // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(p_72367_1_); ++ playerQuitEvent = new PlayerQuitEvent(this.cserver.getPlayer(p_72367_1_), "\u00A7e" + p_72367_1_.getCommandSenderName() + " left the game."); ++ this.cserver.getPluginManager().callEvent(playerQuitEvent); ++ p_72367_1_.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); ++ // CraftBukkit end ++ } ++ // Cauldron end ++ FMLCommonHandler.instance().firePlayerLoggedOut(p_72367_1_); + this.writePlayerData(p_72367_1_); + WorldServer worldserver = p_72367_1_.getServerForPlayer(); + +- if (p_72367_1_.ridingEntity != null) ++ if (p_72367_1_.ridingEntity != null && !(p_72367_1_.ridingEntity instanceof EntityPlayerMP)) // CraftBukkit - Don't remove players + { + worldserver.removePlayerEntityDangerously(p_72367_1_.ridingEntity); + logger.debug("removing player mount"); +@@ -316,9 +437,35 @@ + worldserver.removeEntity(p_72367_1_); + worldserver.getPlayerManager().removePlayer(p_72367_1_); + this.playerEntityList.remove(p_72367_1_); +- this.field_148547_k.remove(p_72367_1_.getUniqueID()); +- net.minecraftforge.common.chunkio.ChunkIOExecutor.adjustPoolSize(this.getCurrentPlayerCount()); +- this.sendPacketToAllPlayers(new S38PacketPlayerListItem(p_72367_1_.getCommandSenderName(), false, 9999)); ++ this.field_148547_k.remove(p_72367_1_.getCommandSenderName()); ++ ChunkIOExecutor.adjustPoolSize(this.getCurrentPlayerCount()); // CraftBukkit ++ // CraftBukkit start - .name -> .listName, replace sendAll with loop ++ // this.sendAll(new PacketPlayOutPlayerInfo(entityplayermp.getName(), false, 9999)); ++ S38PacketPlayerListItem packet = new S38PacketPlayerListItem(p_72367_1_.listName, false, 9999); ++ ++ for (int i = 0; i < this.playerEntityList.size(); ++i) ++ { ++ EntityPlayerMP entityplayermp1 = (EntityPlayerMP) this.playerEntityList.get(i); ++ ++ if (entityplayermp1.getBukkitEntity().canSee(p_72367_1_.getBukkitEntity())) ++ { ++ entityplayermp1.playerNetServerHandler.sendPacket(packet); ++ } ++ } ++ ++ // This removes the scoreboard (and player reference) for the specific player in the manager ++ this.cserver.getScoreboardManager().removePlayer(p_72367_1_.getBukkitEntity()); ++ // Cauldron start ++ if (playerQuitEvent != null) ++ { ++ return playerQuitEvent.getQuitMessage(); ++ } ++ else ++ { ++ return null; ++ } ++ // Cauldron end ++ // CraftBukkit end + } + + public String allowUserToConnect(SocketAddress p_148542_1_, GameProfile p_148542_2_) +@@ -359,6 +506,71 @@ + } + } + ++ // CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer ++ public EntityPlayerMP attemptLogin(NetHandlerLoginServer loginlistener, GameProfile gameprofile, String hostname) ++ { ++ // Instead of kicking then returning, we need to store the kick reason ++ // in the event, check with plugins to see if it's ok, and THEN kick ++ // depending on the outcome. ++ SocketAddress socketaddress = loginlistener.field_147333_a.getSocketAddress(); ++ EntityPlayerMP entity = new EntityPlayerMP(this.mcServer, this.mcServer.worldServerForDimension(0), gameprofile, new ItemInWorldManager( ++ this.mcServer.worldServerForDimension(0))); ++ Player player = entity.getBukkitEntity(); ++ PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ++ ((java.net.InetSocketAddress) loginlistener.field_147333_a.getRawAddress()).getAddress()); // Spigot ++ String s; ++ ++ if (this.bannedPlayers.func_152702_a(gameprofile) && !this.bannedPlayers.func_152683_b(gameprofile).hasBanExpired()) ++ { ++ UserListBansEntry banentry = (UserListBansEntry) this.bannedPlayers.func_152683_b(gameprofile); ++ s = "You are banned from this server!\nReason: " + banentry.getBanReason(); ++ ++ if (banentry.getBanEndDate() != null) ++ { ++ s = s + "\nYour ban will be removed on " + dateFormat.format(banentry.getBanEndDate()); ++ } ++ ++ // return s; ++ event.disallow(PlayerLoginEvent.Result.KICK_BANNED, s); ++ } ++ else if (!this.func_152607_e(gameprofile)) ++ { ++ // return "You are not white-listed on this server!"; ++ event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, org.spigotmc.SpigotConfig.whitelistMessage); // Spigot ++ } ++ else if (this.bannedIPs.func_152708_a(socketaddress) && !this.bannedPlayers.func_152683_b(gameprofile).hasBanExpired()) ++ { ++ IPBanEntry ipbanentry = this.bannedIPs.func_152709_b(socketaddress); ++ s = "Your IP address is banned from this server!\nReason: " + ipbanentry.getBanReason(); ++ ++ if (ipbanentry.getBanEndDate() != null) ++ { ++ s = s + "\nYour ban will be removed on " + dateFormat.format(ipbanentry.getBanEndDate()); ++ } ++ // return s; ++ event.disallow(PlayerLoginEvent.Result.KICK_BANNED, s); ++ } ++ else ++ { ++ // return this.players.size() >= this.maxPlayers ? "The server is full!" : null; ++ if (this.playerEntityList.size() >= this.maxPlayers) ++ { ++ event.disallow(PlayerLoginEvent.Result.KICK_FULL, org.spigotmc.SpigotConfig.serverFullMessage); // Spigot ++ } ++ } ++ ++ this.cserver.getPluginManager().callEvent(event); ++ ++ if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) ++ { ++ loginlistener.func_147322_a(event.getKickMessage()); ++ return null; ++ } ++ ++ return entity; ++ // CraftBukkit end ++ } ++ + public EntityPlayerMP createPlayerForUser(GameProfile p_148545_1_) + { + UUID uuid = EntityPlayer.func_146094_a(p_148545_1_); +@@ -397,116 +609,316 @@ + return new EntityPlayerMP(this.mcServer, this.mcServer.worldServerForDimension(0), p_148545_1_, (ItemInWorldManager)object); + } + +- public EntityPlayerMP respawnPlayer(EntityPlayerMP p_72368_1_, int p_72368_2_, boolean p_72368_3_) ++ public EntityPlayerMP processLogin(GameProfile gameprofile, EntityPlayerMP player) // CraftBukkit - added EntityPlayer + { +- World world = mcServer.worldServerForDimension(p_72368_2_); +- if (world == null) ++ ArrayList arraylist = new ArrayList(); ++ EntityPlayerMP entityplayermp; ++ ++ for (int i = 0; i < this.playerEntityList.size(); ++i) + { +- p_72368_2_ = 0; ++ entityplayermp = (EntityPlayerMP) this.playerEntityList.get(i); ++ ++ if (entityplayermp.getCommandSenderName().equalsIgnoreCase(gameprofile.getName())) ++ { ++ arraylist.add(entityplayermp); ++ } + } +- else if (!world.provider.canRespawnHere()) ++ Iterator iterator = arraylist.iterator(); ++ ++ while (iterator.hasNext()) + { +- p_72368_2_ = world.provider.getRespawnDimension(p_72368_1_); ++ entityplayermp = (EntityPlayerMP) iterator.next(); ++ entityplayermp.playerNetServerHandler.kickPlayerFromServer("You logged in from another location"); + } + +- p_72368_1_.getServerForPlayer().getEntityTracker().removePlayerFromTrackers(p_72368_1_); +- p_72368_1_.getServerForPlayer().getEntityTracker().removeEntityFromAllTrackingPlayers(p_72368_1_); +- p_72368_1_.getServerForPlayer().getPlayerManager().removePlayer(p_72368_1_); +- this.playerEntityList.remove(p_72368_1_); +- this.mcServer.worldServerForDimension(p_72368_1_.dimension).removePlayerEntityDangerously(p_72368_1_); +- ChunkCoordinates chunkcoordinates = p_72368_1_.getBedLocation(p_72368_2_); +- boolean flag1 = p_72368_1_.isSpawnForced(p_72368_2_); +- p_72368_1_.dimension = p_72368_2_; ++ /* CraftBukkit start + Object object; + + if (this.mcServer.isDemo()) + { +- object = new DemoWorldManager(this.mcServer.worldServerForDimension(p_72368_1_.dimension)); ++ object = new DemoWorldManager(this.mcServer.worldServerForDimension(0)); + } + else + { +- object = new ItemInWorldManager(this.mcServer.worldServerForDimension(p_72368_1_.dimension)); ++ object = new ItemInWorldManager(this.mcServer.worldServerForDimension(0)); + } + +- EntityPlayerMP entityplayermp1 = new EntityPlayerMP(this.mcServer, this.mcServer.worldServerForDimension(p_72368_1_.dimension), p_72368_1_.getGameProfile(), (ItemInWorldManager)object); +- entityplayermp1.playerNetServerHandler = p_72368_1_.playerNetServerHandler; +- entityplayermp1.clonePlayer(p_72368_1_, p_72368_3_); +- entityplayermp1.dimension = p_72368_2_; +- entityplayermp1.setEntityId(p_72368_1_.getEntityId()); +- WorldServer worldserver = this.mcServer.worldServerForDimension(p_72368_1_.dimension); +- this.func_72381_a(entityplayermp1, p_72368_1_, worldserver); ++ return new EntityPlayerMP(this.mcServer, this.mcServer.worldServerForDimension(0), p_148545_1_, (ItemInWorldManager)object); ++ // */ ++ return player; ++ // CraftBukkit end ++ } ++ ++ // Cauldron start - refactor entire method for sanity. ++ public EntityPlayerMP respawnPlayer(EntityPlayerMP par1EntityPlayerMP, int par2, boolean par3) ++ { ++ return this.respawnPlayer(par1EntityPlayerMP, par2, par3, null); ++ } ++ ++ public EntityPlayerMP respawnPlayer(EntityPlayerMP par1EntityPlayerMP, int targetDimension, boolean returnFromEnd, Location location) ++ { ++ // Phase 1 - check if the player is allowed to respawn in same dimension ++ World world = mcServer.worldServerForDimension(targetDimension); ++ org.bukkit.World fromWorld = par1EntityPlayerMP.getBukkitEntity().getWorld(); ++ ++ if (world == null) ++ { ++ targetDimension = 0; ++ } ++ else if (location == null && !world.provider.canRespawnHere()) // ignore plugins ++ { ++ targetDimension = world.provider.getRespawnDimension(par1EntityPlayerMP); ++ } ++ ++ // Phase 2 - handle return from End ++ if (returnFromEnd) ++ { ++ WorldServer exitWorld = this.mcServer.worldServerForDimension(targetDimension); ++ Location enter = par1EntityPlayerMP.getBukkitEntity().getLocation(); ++ Location exit = null; ++ // THE_END -> NORMAL; use bed if available, otherwise default spawn ++ exit = ((org.bukkit.craftbukkit.entity.CraftPlayer) par1EntityPlayerMP.getBukkitEntity()).getBedSpawnLocation(); ++ ++ if (exit == null || ((CraftWorld) exit.getWorld()).getHandle().dimension != 0) ++ { ++ exit = exitWorld.getWorld().getSpawnLocation(); ++ } ++ PlayerPortalEvent event = new PlayerPortalEvent(par1EntityPlayerMP.getBukkitEntity(), enter, exit, org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT, ++ TeleportCause.END_PORTAL); ++ event.useTravelAgent(false); ++ Bukkit.getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled() || event.getTo() == null) ++ { ++ return null; ++ } ++ } ++ ++ // Phase 3 - remove current player from current dimension ++ par1EntityPlayerMP.getServerForPlayer().getEntityTracker().removePlayerFromTrackers(par1EntityPlayerMP); ++ // par1EntityPlayerMP.getServerForPlayer().getEntityTracker().removeEntityFromAllTrackingPlayers(par1EntityPlayerMP); // CraftBukkit ++ par1EntityPlayerMP.getServerForPlayer().getPlayerManager().removePlayer(par1EntityPlayerMP); ++ this.playerEntityList.remove(par1EntityPlayerMP); ++ this.mcServer.worldServerForDimension(par1EntityPlayerMP.dimension).removePlayerEntityDangerously(par1EntityPlayerMP); ++ ++ // Phase 4 - handle bed spawn ++ ChunkCoordinates bedSpawnChunkCoords = par1EntityPlayerMP.getBedLocation(targetDimension); ++ boolean spawnForced = par1EntityPlayerMP.isSpawnForced(targetDimension); ++ par1EntityPlayerMP.dimension = targetDimension; ++ // CraftBukkit start ++ EntityPlayerMP entityplayermp1 = par1EntityPlayerMP; ++ entityplayermp1.setWorld(this.mcServer.worldServerForDimension(par1EntityPlayerMP.dimension)); // make sure to update reference for bed spawn logic ++ entityplayermp1.playerConqueredTheEnd = false; + ChunkCoordinates chunkcoordinates1; ++ boolean isBedSpawn = false; ++ org.bukkit.World toWorld = entityplayermp1.getBukkitEntity().getWorld(); + +- if (chunkcoordinates != null) ++ if (location == null) // use bed logic only if player respawns (player death) + { +- chunkcoordinates1 = EntityPlayer.verifyRespawnCoordinates(this.mcServer.worldServerForDimension(p_72368_1_.dimension), chunkcoordinates, flag1); ++ if (bedSpawnChunkCoords != null) // if player has a bed ++ { ++ chunkcoordinates1 = EntityPlayer.verifyRespawnCoordinates(this.mcServer.worldServerForDimension(par1EntityPlayerMP.dimension), ++ bedSpawnChunkCoords, spawnForced); + +- if (chunkcoordinates1 != null) ++ if (chunkcoordinates1 != null) ++ { ++ isBedSpawn = true; ++ entityplayermp1.setLocationAndAngles((double) ((float) chunkcoordinates1.posX + 0.5F), (double) ((float) chunkcoordinates1.posY + 0.1F), ++ (double) ((float) chunkcoordinates1.posZ + 0.5F), 0.0F, 0.0F); ++ entityplayermp1.setSpawnChunk(bedSpawnChunkCoords, spawnForced); ++ location = new Location(toWorld, bedSpawnChunkCoords.posX + 0.5, bedSpawnChunkCoords.posY, bedSpawnChunkCoords.posZ + 0.5); ++ } ++ else ++ // bed was not found (broken) ++ { ++ //entityplayermp1.setSpawnChunk(null, true); // CraftBukkit ++ entityplayermp1.playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(0, 0)); ++ location = new Location(toWorld, toWorld.getSpawnLocation().getX(), toWorld.getSpawnLocation().getY(), toWorld.getSpawnLocation().getZ()); // use the spawnpoint as location ++ } ++ } ++ ++ if (location == null) + { +- entityplayermp1.setLocationAndAngles((double)((float)chunkcoordinates1.posX + 0.5F), (double)((float)chunkcoordinates1.posY + 0.1F), (double)((float)chunkcoordinates1.posZ + 0.5F), 0.0F, 0.0F); +- entityplayermp1.setSpawnChunk(chunkcoordinates, flag1); ++ location = new Location(toWorld, toWorld.getSpawnLocation().getX(), toWorld.getSpawnLocation().getY(), toWorld.getSpawnLocation().getZ()); // use the world spawnpoint as default location + } +- else ++ ++ Player respawnPlayer = this.cserver.getPlayer(entityplayermp1); ++ PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn); ++ this.cserver.getPluginManager().callEvent(respawnEvent); ++ ++ if (!spawnForced) // mods override plugins + { +- entityplayermp1.playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(0, 0.0F)); ++ location = respawnEvent.getRespawnLocation(); + } ++ ++ par1EntityPlayerMP.reset(); + } ++ else ++ // plugin ++ { ++ location.setWorld(this.mcServer.worldServerForDimension(targetDimension).getWorld()); ++ } + +- worldserver.theChunkProviderServer.loadChunk((int)entityplayermp1.posX >> 4, (int)entityplayermp1.posZ >> 4); ++ WorldServer targetWorld = ((CraftWorld) location.getWorld()).getHandle(); ++ entityplayermp1.setPositionAndRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); ++ // CraftBukkit end ++ targetWorld.theChunkProviderServer.loadChunk((int) entityplayermp1.posX >> 4, (int) entityplayermp1.posZ >> 4); + +- while (!worldserver.getCollidingBoundingBoxes(entityplayermp1, entityplayermp1.boundingBox).isEmpty()) ++ while (!targetWorld.getCollidingBoundingBoxes(entityplayermp1, entityplayermp1.boundingBox).isEmpty()) + { + entityplayermp1.setPosition(entityplayermp1.posX, entityplayermp1.posY + 1.0D, entityplayermp1.posZ); + } + +- entityplayermp1.playerNetServerHandler.sendPacket(new S07PacketRespawn(entityplayermp1.dimension, entityplayermp1.worldObj.difficultySetting, entityplayermp1.worldObj.getWorldInfo().getTerrainType(), entityplayermp1.theItemInWorldManager.getGameType())); +- chunkcoordinates1 = worldserver.getSpawnPoint(); +- entityplayermp1.playerNetServerHandler.setPlayerLocation(entityplayermp1.posX, entityplayermp1.posY, entityplayermp1.posZ, entityplayermp1.rotationYaw, entityplayermp1.rotationPitch); ++ // Phase 5 - Respawn player in new world ++ int actualDimension = targetWorld.provider.dimensionId; ++ // Cauldron - change dim for bukkit added dimensions ++ if (DimensionManager.isBukkitDimension(actualDimension)) ++ { ++ FMLEmbeddedChannel serverChannel = ForgeNetworkHandler.getServerChannel(); ++ serverChannel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.PLAYER); ++ serverChannel.attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(entityplayermp1); ++ serverChannel.writeOutbound(new ForgeMessage.DimensionRegisterMessage(actualDimension, targetWorld.getWorld().getEnvironment().getId())); ++ } ++ // Cauldron end ++ // CraftBukkit start ++ entityplayermp1.playerNetServerHandler.sendPacket(new S07PacketRespawn(actualDimension, targetWorld.difficultySetting, targetWorld.getWorldInfo() ++ .getTerrainType(), entityplayermp1.theItemInWorldManager.getGameType())); ++ entityplayermp1.setWorld(targetWorld); // in case plugin changed it ++ entityplayermp1.isDead = false; ++ entityplayermp1.playerNetServerHandler.teleport(new Location(targetWorld.getWorld(), entityplayermp1.posX, entityplayermp1.posY, entityplayermp1.posZ, ++ entityplayermp1.rotationYaw, entityplayermp1.rotationPitch)); ++ entityplayermp1.setSneaking(false); ++ chunkcoordinates1 = targetWorld.getSpawnPoint(); ++ // CraftBukkit end + entityplayermp1.playerNetServerHandler.sendPacket(new S05PacketSpawnPosition(chunkcoordinates1.posX, chunkcoordinates1.posY, chunkcoordinates1.posZ)); +- entityplayermp1.playerNetServerHandler.sendPacket(new S1FPacketSetExperience(entityplayermp1.experience, entityplayermp1.experienceTotal, entityplayermp1.experienceLevel)); +- this.updateTimeAndWeatherForPlayer(entityplayermp1, worldserver); +- worldserver.getPlayerManager().addPlayer(entityplayermp1); +- worldserver.spawnEntityInWorld(entityplayermp1); ++ entityplayermp1.playerNetServerHandler.sendPacket(new S1FPacketSetExperience(entityplayermp1.experience, entityplayermp1.experienceTotal, ++ entityplayermp1.experienceLevel)); ++ this.updateTimeAndWeatherForPlayer(entityplayermp1, targetWorld); ++ targetWorld.getPlayerManager().addPlayer(entityplayermp1); ++ targetWorld.spawnEntityInWorld(entityplayermp1); + this.playerEntityList.add(entityplayermp1); + entityplayermp1.addSelfToInternalCraftingInventory(); + entityplayermp1.setHealth(entityplayermp1.getHealth()); +- FMLCommonHandler.instance().firePlayerRespawnEvent(entityplayermp1); ++ // If world changed then fire the appropriate change world event else respawn ++ if (fromWorld != location.getWorld()) ++ { ++ FMLCommonHandler.instance().firePlayerChangedDimensionEvent(entityplayermp1, ((CraftWorld) fromWorld).getHandle().provider.dimensionId, ++ ((CraftWorld) location.getWorld()).getHandle().provider.dimensionId); // Cauldron - fire forge changed dimension event ++ } ++ else ++ FMLCommonHandler.instance().firePlayerRespawnEvent(entityplayermp1); + return entityplayermp1; + } + +- public void transferPlayerToDimension(EntityPlayerMP p_72356_1_, int p_72356_2_) ++ // Cauldron start - refactor transferPlayerToDimension to be compatible with Bukkit. These methods are to be used when a player comes in contact with a portal ++ public void transferPlayerToDimension(EntityPlayerMP p_72356_1_, int p_72356_2_) // wrapper for vanilla compatibility + { + transferPlayerToDimension(p_72356_1_, p_72356_2_, mcServer.worldServerForDimension(p_72356_2_).getDefaultTeleporter()); + } + +- public void transferPlayerToDimension(EntityPlayerMP p_72356_1_, int p_72356_2_, Teleporter teleporter) ++ public void transferPlayerToDimension(EntityPlayerMP p_72356_1_, int p_72356_2_, Teleporter teleporter) // mods such as Twilight Forest call this method directly + { +- int j = p_72356_1_.dimension; +- WorldServer worldserver = this.mcServer.worldServerForDimension(p_72356_1_.dimension); +- p_72356_1_.dimension = p_72356_2_; +- WorldServer worldserver1 = this.mcServer.worldServerForDimension(p_72356_1_.dimension); +- p_72356_1_.playerNetServerHandler.sendPacket(new S07PacketRespawn(p_72356_1_.dimension, worldserver1.difficultySetting, worldserver1.getWorldInfo().getTerrainType(), p_72356_1_.theItemInWorldManager.getGameType())); // Forge: Use new dimensions information +- worldserver.removePlayerEntityDangerously(p_72356_1_); +- p_72356_1_.isDead = false; +- this.transferEntityToWorld(p_72356_1_, j, worldserver, worldserver1, teleporter); +- this.func_72375_a(p_72356_1_, worldserver); +- p_72356_1_.playerNetServerHandler.setPlayerLocation(p_72356_1_.posX, p_72356_1_.posY, p_72356_1_.posZ, p_72356_1_.rotationYaw, p_72356_1_.rotationPitch); +- p_72356_1_.theItemInWorldManager.setWorld(worldserver1); +- this.updateTimeAndWeatherForPlayer(p_72356_1_, worldserver1); +- this.syncPlayerInventory(p_72356_1_); +- Iterator iterator = p_72356_1_.getActivePotionEffects().iterator(); ++ this.transferPlayerToDimension(p_72356_1_, p_72356_2_, teleporter, TeleportCause.MOD); // use our mod cause ++ } + ++ public void transferPlayerToDimension(EntityPlayerMP par1EntityPlayerMP, int par2, TeleportCause cause) ++ { ++ this.transferPlayerToDimension(par1EntityPlayerMP, par2, mcServer.worldServerForDimension(par2).getDefaultTeleporter(), cause); ++ } ++ ++ public void transferPlayerToDimension(EntityPlayerMP par1EntityPlayerMP, int targetDimension, Teleporter teleporter, TeleportCause cause) // Cauldron - add TeleportCause ++ { ++ // Allow Forge hotloading on teleport ++ WorldServer fromWorld = this.mcServer.worldServerForDimension(par1EntityPlayerMP.dimension); ++ WorldServer exitWorld = this.mcServer.worldServerForDimension(targetDimension); ++ ++ // CraftBukkit start - Replaced the standard handling of portals with a more customised method. ++ Location enter = par1EntityPlayerMP.getBukkitEntity().getLocation(); ++ Location exit = null; ++ boolean useTravelAgent = false; ++ ++ if (exitWorld != null) ++ { ++ exit = this.calculateTarget(enter, exitWorld); ++ if (cause != cause.MOD) // don't use travel agent for custom dimensions ++ { ++ useTravelAgent = true; ++ } ++ } ++ ++ // allow forge mods to be the teleporter ++ TravelAgent agent = null; ++ if (exit != null && teleporter == null) ++ { ++ teleporter = ((CraftWorld) exit.getWorld()).getHandle().getDefaultTeleporter(); ++ if (teleporter instanceof TravelAgent) ++ { ++ agent = (TravelAgent) teleporter; ++ } ++ } ++ else ++ { ++ if (teleporter instanceof TravelAgent) ++ { ++ agent = (TravelAgent) teleporter; ++ } ++ } ++ if (agent == null) // mod teleporter such as Twilight Forest ++ { ++ agent = org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins ++ } ++ ++ PlayerPortalEvent event = new PlayerPortalEvent(par1EntityPlayerMP.getBukkitEntity(), enter, exit, agent, cause); ++ event.useTravelAgent(useTravelAgent); ++ Bukkit.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled() || event.getTo() == null) ++ { ++ return; ++ } ++ ++ exit = event.useTravelAgent() && cause != cause.MOD ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); // make sure plugins don't override travelagent for mods ++ ++ if (exit == null) ++ { ++ return; ++ } ++ ++ exitWorld = ((CraftWorld) exit.getWorld()).getHandle(); ++ Vector velocity = par1EntityPlayerMP.getBukkitEntity().getVelocity(); ++ boolean before = exitWorld.theChunkProviderServer.loadChunkOnProvideRequest; ++ exitWorld.theChunkProviderServer.loadChunkOnProvideRequest = true; ++ exitWorld.getDefaultTeleporter().adjustExit(par1EntityPlayerMP, exit, velocity); ++ exitWorld.theChunkProviderServer.loadChunkOnProvideRequest = before; ++ // CraftBukkit end ++ ++ par1EntityPlayerMP.dimension = targetDimension; ++ par1EntityPlayerMP.playerNetServerHandler.sendPacket(new S07PacketRespawn(par1EntityPlayerMP.dimension, par1EntityPlayerMP.worldObj.difficultySetting, ++ par1EntityPlayerMP.worldObj.getWorldInfo().getTerrainType(), par1EntityPlayerMP.theItemInWorldManager.getGameType())); ++ fromWorld.removePlayerEntityDangerously(par1EntityPlayerMP); ++ par1EntityPlayerMP.isDead = false; ++ this.transferEntityToWorld(par1EntityPlayerMP, fromWorld.provider.dimensionId, fromWorld, exitWorld, teleporter); ++ this.func_72375_a(par1EntityPlayerMP, fromWorld); ++ par1EntityPlayerMP.playerNetServerHandler.setPlayerLocation(par1EntityPlayerMP.posX, par1EntityPlayerMP.posY, par1EntityPlayerMP.posZ, ++ par1EntityPlayerMP.rotationYaw, par1EntityPlayerMP.rotationPitch); ++ par1EntityPlayerMP.theItemInWorldManager.setWorld(exitWorld); ++ this.updateTimeAndWeatherForPlayer(par1EntityPlayerMP, exitWorld); ++ this.syncPlayerInventory(par1EntityPlayerMP); ++ Iterator iterator = par1EntityPlayerMP.getActivePotionEffects().iterator(); ++ + while (iterator.hasNext()) + { + PotionEffect potioneffect = (PotionEffect)iterator.next(); +- p_72356_1_.playerNetServerHandler.sendPacket(new S1DPacketEntityEffect(p_72356_1_.getEntityId(), potioneffect)); ++ par1EntityPlayerMP.playerNetServerHandler.sendPacket(new S1DPacketEntityEffect(par1EntityPlayerMP.getEntityId(), potioneffect)); + } +- FMLCommonHandler.instance().firePlayerChangedDimensionEvent(p_72356_1_, j, p_72356_2_); ++ FMLCommonHandler.instance().firePlayerChangedDimensionEvent(par1EntityPlayerMP, fromWorld.dimension, targetDimension); + } + + public void transferEntityToWorld(Entity p_82448_1_, int p_82448_2_, WorldServer p_82448_3_, WorldServer p_82448_4_) + { +- transferEntityToWorld(p_82448_1_, p_82448_2_, p_82448_3_, p_82448_4_, p_82448_4_.getDefaultTeleporter()); ++ // CraftBukkit start - Split into modular functions ++ //transferEntityToWorld(p_82448_1_, p_82448_2_, p_82448_3_, p_82448_4_, p_82448_4_.getDefaultTeleporter()); ++ Location exit = this.calculateTarget(p_82448_1_.getBukkitEntity().getLocation(), p_82448_4_); ++ this.repositionEntity(p_82448_1_, exit, true); + } + + public void transferEntityToWorld(Entity p_82448_1_, int p_82448_2_, WorldServer p_82448_3_, WorldServer p_82448_4_, Teleporter teleporter) +@@ -592,6 +1004,197 @@ + p_82448_1_.setWorld(p_82448_4_); + } + ++ // Copy of original a(Entity, int, WorldServer, WorldServer) method with only location calculation logic ++ public Location calculateTarget(Location enter, World target) ++ { ++ WorldServer worldserver = ((CraftWorld) enter.getWorld()).getHandle(); ++ WorldServer worldserver1 = ((CraftWorld) target.getWorld()).getHandle(); ++ int i = worldserver.dimension; ++ double y = enter.getY(); ++ float yaw = enter.getYaw(); ++ float pitch = enter.getPitch(); ++ double d0 = enter.getX(); ++ double d1 = enter.getZ(); ++ double d2 = 8.0D; ++ ++ /* ++ double d3 = entity.locX; ++ double d4 = entity.locY; ++ double d5 = entity.locZ; ++ float f = entity.yaw; ++ ++ worldserver.methodProfiler.a("moving"); ++ */ ++ if (worldserver1.dimension == -1) ++ { ++ d0 /= d2; ++ d1 /= d2; ++ /* ++ entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ */ ++ } ++ else if (worldserver1.dimension == 0) ++ { ++ d0 *= d2; ++ d1 *= d2; ++ /* ++ entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ */ ++ } ++ else ++ { ++ ChunkCoordinates chunkcoordinates; ++ ++ if (i == 1) ++ { ++ // use default NORMAL world spawn instead of target ++ worldserver1 = this.mcServer.worlds.get(0); ++ chunkcoordinates = worldserver1.getSpawnPoint(); ++ } ++ else ++ { ++ chunkcoordinates = worldserver1.getEntrancePortalLocation(); ++ } ++ ++ // Cauldron start - validate chunkcoordinates ++ if (chunkcoordinates != null) ++ { ++ d0 = (double) chunkcoordinates.posX; ++ y = (double) chunkcoordinates.posY; ++ d1 = (double) chunkcoordinates.posZ; ++ yaw = 90.0F; ++ pitch = 0.0F; ++ } ++ // Cauldron end ++ /* ++ entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ */ ++ } ++ ++ // worldserver.methodProfiler.b(); ++ if (i != 1) ++ { ++ // worldserver.methodProfiler.a("placing"); ++ d0 = (double) MathHelper.clamp_int((int) d0, -29999872, 29999872); ++ d1 = (double) MathHelper.clamp_int((int) d1, -29999872, 29999872); ++ /* ++ if (entity.isAlive()) { ++ worldserver1.addEntity(entity); ++ entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ worldserver1.entityJoinedWorld(entity, false); ++ worldserver1.t().a(entity, d3, d4, d5, f); ++ } ++ ++ worldserver.methodProfiler.b(); ++ */ ++ } ++ ++ // entity.spawnIn(worldserver1); ++ return new Location(worldserver1.getWorld(), d0, y, d1, yaw, pitch); ++ } ++ ++ // copy of original a(Entity, int, WorldServer, WorldServer) method with only entity repositioning logic ++ public void repositionEntity(Entity entity, Location exit, boolean portal) ++ { ++ int i = entity.dimension; ++ WorldServer worldserver = (WorldServer) entity.worldObj; ++ WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); ++ /* ++ double d0 = entity.locX; ++ double d1 = entity.locZ; ++ double d2 = 8.0D; ++ double d3 = entity.locX; ++ double d4 = entity.locY; ++ double d5 = entity.locZ; ++ float f = entity.yaw; ++ */ ++ worldserver.theProfiler.startSection("moving"); ++ entity.setLocationAndAngles(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); ++ ++ if (entity.isEntityAlive()) ++ { ++ worldserver.updateEntityWithOptionalForce(entity, false); ++ } ++ ++ /* ++ if (entity.dimension == -1) { ++ d0 /= d2; ++ d1 /= d2; ++ entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ } else if (entity.dimension == 0) { ++ d0 *= d2; ++ d1 *= d2; ++ entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ } else { ++ ChunkCoordinates chunkcoordinates; ++ ++ if (i == 1) { ++ chunkcoordinates = worldserver1.getSpawn(); ++ } else { ++ chunkcoordinates = worldserver1.getDimensionSpawn(); ++ } ++ ++ d0 = (double) chunkcoordinates.x; ++ entity.locY = (double) chunkcoordinates.y; ++ d1 = (double) chunkcoordinates.z; ++ entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ } ++ */ ++ worldserver.theProfiler.endSection(); ++ ++ if (i != 1) ++ { ++ worldserver.theProfiler.startSection("placing"); ++ ++ /* ++ d0 = (double) MathHelper.a((int) d0, -29999872, 29999872); ++ d1 = (double) MathHelper.a((int) d1, -29999872, 29999872); ++ */ ++ if (entity.isEntityAlive()) ++ { ++ // entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch) ++ // worldserver1.s().a(entity, d3, d4, d5, f); ++ if (portal) ++ { ++ Vector velocity = entity.getBukkitEntity().getVelocity(); ++ worldserver1.getDefaultTeleporter().adjustExit(entity, exit, velocity); // Should be getTravelAgent ++ entity.setLocationAndAngles(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); ++ ++ if (entity.motionX != velocity.getX() || entity.motionY != velocity.getY() || entity.motionZ != velocity.getZ()) ++ { ++ entity.getBukkitEntity().setVelocity(velocity); ++ } ++ } ++ ++ worldserver1.spawnEntityInWorld(entity); ++ worldserver1.updateEntityWithOptionalForce(entity, false); ++ } ++ ++ worldserver.theProfiler.endSection(); ++ } ++ ++ entity.setWorld(worldserver1); ++ // CraftBukkit end ++ } ++ + public void sendPlayerInfoToAllPlayers() + { + if (++this.playerPingIndex > 600) +@@ -599,11 +1202,13 @@ + this.playerPingIndex = 0; + } + ++ /* CraftBukkit start - Remove updating of lag to players -- it spams way to much on big servers. + if (this.playerPingIndex < this.playerEntityList.size()) + { + EntityPlayerMP entityplayermp = (EntityPlayerMP)this.playerEntityList.get(this.playerPingIndex); + this.sendPacketToAllPlayers(new S38PacketPlayerListItem(entityplayermp.getCommandSenderName(), true, entityplayermp.ping)); + } ++ // CraftBukkit end */ + } + + public void sendPacketToAllPlayers(Packet p_148540_1_) +@@ -864,13 +1469,24 @@ + for (int j = 0; j < this.playerEntityList.size(); ++j) + { + EntityPlayerMP entityplayermp = (EntityPlayerMP)this.playerEntityList.get(j); +- ++ // CraftBukkit start - Test if player receiving packet can see the source of the packet ++ if (p_148543_1_ != null && p_148543_1_ instanceof EntityPlayerMP ++ && !entityplayermp.getBukkitEntity().canSee(((EntityPlayerMP) p_148543_1_).getBukkitEntity())) ++ { ++ continue; ++ } ++ // CraftBukkit end + if (entityplayermp != p_148543_1_ && entityplayermp.dimension == p_148543_10_) + { + double d4 = p_148543_2_ - entityplayermp.posX; + double d5 = p_148543_4_ - entityplayermp.posY; + double d6 = p_148543_6_ - entityplayermp.posZ; +- ++ // Cauldron start - send packets only to players within configured player tracking range) ++ if (p_148543_8_ > org.spigotmc.TrackingRange.getEntityTrackingRange(entityplayermp, 512)) ++ { ++ p_148543_8_ = org.spigotmc.TrackingRange.getEntityTrackingRange(entityplayermp, 512); ++ } ++ // Cauldron end + if (d4 * d4 + d5 * d5 + d6 * d6 < p_148543_8_ * p_148543_8_) + { + entityplayermp.playerNetServerHandler.sendPacket(p_148543_11_); +@@ -925,16 +1541,19 @@ + + if (p_72354_2_.isRaining()) + { ++ // CraftBukkit start - handle player weather + p_72354_1_.playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(1, 0.0F)); + p_72354_1_.playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(7, p_72354_2_.getRainStrength(1.0F))); + p_72354_1_.playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(8, p_72354_2_.getWeightedThunderStrength(1.0F))); ++ p_72354_1_.setPlayerWeather(org.bukkit.WeatherType.DOWNFALL, false); ++ // CraftBukkit end + } + } + + public void syncPlayerInventory(EntityPlayerMP p_72385_1_) + { + p_72385_1_.sendContainerToPlayer(p_72385_1_.inventoryContainer); +- p_72385_1_.setPlayerHealthUpdated(); ++ p_72385_1_.getBukkitEntity().updateScaledHealth(); // CraftBukkit - Update scaled health on respawn and worldchange + p_72385_1_.playerNetServerHandler.sendPacket(new S09PacketHeldItemChange(p_72385_1_.inventory.currentItem)); + } + +@@ -950,7 +1569,10 @@ + + public String[] getAvailablePlayerDat() + { +- return this.mcServer.worldServers[0].getSaveHandler().getSaveHandler().getAvailablePlayerDat(); ++ // Cauldron start - don't crash if the overworld isn't loaded ++ List worldServers = this.mcServer.worlds; ++ return worldServers.isEmpty() ? new String[0] : worldServers.get(0).getSaveHandler().getSaveHandler().getAvailablePlayerDat(); // CraftBukkit ++ // Cauldron end + } + + public void setWhiteListEnabled(boolean p_72371_1_) +@@ -1019,12 +1641,30 @@ + + public void removeAllPlayers() + { +- for (int i = 0; i < this.playerEntityList.size(); ++i) ++ while (!this.playerEntityList.isEmpty()) + { +- ((EntityPlayerMP)this.playerEntityList.get(i)).playerNetServerHandler.kickPlayerFromServer("Server closed"); ++ // Spigot start ++ EntityPlayerMP p = (EntityPlayerMP) this.playerEntityList.get(0); ++ p.playerNetServerHandler.kickPlayerFromServer(this.mcServer.server.getShutdownMessage()); ++ ++ if ((!this.playerEntityList.isEmpty()) && (this.playerEntityList.get(0) == p)) ++ { ++ this.playerEntityList.remove(0); // Prevent shutdown hang if already disconnected ++ } ++ // Spigot end + } + } + ++ // CraftBukkit start - Support multi-line messages ++ public void sendMessage(IChatComponent[] ichatbasecomponent) ++ { ++ for (IChatComponent component : ichatbasecomponent) ++ { ++ sendChatMsgImpl(component, true); ++ } ++ } ++ // CraftBukkit end ++ + public void sendChatMsgImpl(IChatComponent p_148544_1_, boolean p_148544_2_) + { + this.mcServer.addChatMessage(p_148544_1_); diff --git a/patches/net/minecraft/server/management/UserList.java.patch b/patches/net/minecraft/server/management/UserList.java.patch new file mode 100644 index 0000000..ca6b34a --- /dev/null +++ b/patches/net/minecraft/server/management/UserList.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/server/management/UserList.java ++++ ../src-work/minecraft/net/minecraft/server/management/UserList.java +@@ -184,6 +184,12 @@ + return this.field_152696_d.size() < 1; + } + ++ // CraftBukkit start ++ public Collection getValues() { ++ return this.field_152696_d.values(); ++ } ++ // CraftBukkit end ++ + @SideOnly(Side.SERVER) + public void func_152679_g() throws IOException + { diff --git a/patches/net/minecraft/server/management/UserListEntry.java.patch b/patches/net/minecraft/server/management/UserListEntry.java.patch new file mode 100644 index 0000000..d098c36 --- /dev/null +++ b/patches/net/minecraft/server/management/UserListEntry.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/server/management/UserListEntry.java ++++ ../src-work/minecraft/net/minecraft/server/management/UserListEntry.java +@@ -17,7 +17,7 @@ + this.field_152642_a = p_i1147_1_; + } + +- Object func_152640_f() ++ public Object func_152640_f() // CraftBukkit - private -> public + { + return this.field_152642_a; + } diff --git a/patches/net/minecraft/server/management/UserListOps.java.patch b/patches/net/minecraft/server/management/UserListOps.java.patch new file mode 100644 index 0000000..8cda80d --- /dev/null +++ b/patches/net/minecraft/server/management/UserListOps.java.patch @@ -0,0 +1,10 @@ +--- ../src-base/minecraft/net/minecraft/server/management/UserListOps.java ++++ ../src-work/minecraft/net/minecraft/server/management/UserListOps.java +@@ -35,6 +35,7 @@ + + protected String func_152699_b(GameProfile p_152699_1_) + { ++ if (p_152699_1_ == null || p_152699_1_.getId() == null) return "invalid"; // Cauldron - handle GameProfiles with no ID + return p_152699_1_.getId().toString(); + } + diff --git a/patches/net/minecraft/server/network/NetHandlerHandshakeTCP.java.patch b/patches/net/minecraft/server/network/NetHandlerHandshakeTCP.java.patch new file mode 100644 index 0000000..e961924 --- /dev/null +++ b/patches/net/minecraft/server/network/NetHandlerHandshakeTCP.java.patch @@ -0,0 +1,122 @@ +--- ../src-base/minecraft/net/minecraft/server/network/NetHandlerHandshakeTCP.java ++++ ../src-work/minecraft/net/minecraft/server/network/NetHandlerHandshakeTCP.java +@@ -3,6 +3,7 @@ + import cpw.mods.fml.common.FMLCommonHandler; + import io.netty.util.concurrent.GenericFutureListener; + import net.minecraft.network.EnumConnectionState; ++import net.minecraft.network.INetHandler; + import net.minecraft.network.NetworkManager; + import net.minecraft.network.handshake.INetHandlerHandshakeServer; + import net.minecraft.network.handshake.client.C00Handshake; +@@ -11,8 +12,24 @@ + import net.minecraft.util.ChatComponentText; + import net.minecraft.util.IChatComponent; + ++ ++// CraftBukkit start ++import java.net.InetAddress; ++import java.util.HashMap; ++// CraftBukkit end ++// Spigot start ++import com.mojang.authlib.properties.Property; ++import com.mojang.util.UUIDTypeAdapter; ++// Spigot end ++ + public class NetHandlerHandshakeTCP implements INetHandlerHandshakeServer + { ++ private static final com.google.gson.Gson gson = new com.google.gson.Gson(); // Spigot ++ // CraftBukkit start ++ private static final HashMap throttleTracker = new HashMap(); ++ private static int throttleCounter = 0; ++ // CraftBukkit end ++ + private final MinecraftServer field_147387_a; + private final NetworkManager field_147386_b; + private static final String __OBFID = "CL_00001456"; +@@ -39,6 +56,52 @@ + this.field_147386_b.setConnectionState(EnumConnectionState.LOGIN); + ChatComponentText chatcomponenttext; + ++ // CraftBukkit start ++ try ++ { ++ long currentTime = System.currentTimeMillis(); ++ long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle(); ++ InetAddress address = ((java.net.InetSocketAddress) this.field_147386_b.getSocketAddress()).getAddress(); ++ ++ synchronized (throttleTracker) ++ { ++ if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.get(address) < connectionThrottle) ++ { ++ throttleTracker.put(address, currentTime); ++ chatcomponenttext = new ChatComponentText("Connection throttled! Please wait before reconnecting."); ++ this.field_147386_b.scheduleOutboundPacket(new S00PacketDisconnect(chatcomponenttext), new GenericFutureListener[0]); ++ this.field_147386_b.closeChannel(chatcomponenttext); // Should be close ++ return; ++ } ++ ++ throttleTracker.put(address, currentTime); ++ throttleCounter++; ++ ++ if (throttleCounter > 200) ++ { ++ throttleCounter = 0; ++ // Cleanup stale entries ++ java.util.Iterator iter = throttleTracker.entrySet().iterator(); ++ ++ while (iter.hasNext()) ++ { ++ java.util.Map.Entry entry = (java.util.Map.Entry) iter.next(); ++ ++ if (entry.getValue() > connectionThrottle) ++ { ++ iter.remove(); ++ } ++ } ++ } ++ } ++ } ++ catch (Throwable t) ++ { ++ org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t); ++ } ++ ++ // CraftBukkit end ++ + if (p_147383_1_.func_149595_d() > 5) + { + chatcomponenttext = new ChatComponentText("Outdated server! I\'m still on 1.7.10"); +@@ -54,6 +117,33 @@ + else + { + this.field_147386_b.setNetHandler(new NetHandlerLoginServer(this.field_147387_a, this.field_147386_b)); ++ ++ // Spigot Start ++ if (org.spigotmc.SpigotConfig.bungee) ++ { ++ String[] split = p_147383_1_.field_149598_b.split("\00"); ++ ++ if (split.length == 3 || split.length == 4) ++ { ++ p_147383_1_.field_149598_b = split[0]; ++ field_147386_b.socketAddress = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) field_147386_b.getSocketAddress()).getPort()); ++ field_147386_b.spoofedUUID = UUIDTypeAdapter.fromString( split[2] ); ++ } ++ else ++ { ++ chatcomponenttext = new ChatComponentText("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!"); ++ this.field_147386_b.scheduleOutboundPacket(new S00PacketDisconnect(chatcomponenttext), new GenericFutureListener[0]); ++ this.field_147386_b.closeChannel(chatcomponenttext); ++ return; ++ } ++ ++ if (split.length == 4) ++ { ++ field_147386_b.spoofedProfile = gson.fromJson(split[3], Property[].class); ++ } ++ } ++ // Spigot End ++ ((NetHandlerLoginServer) this.field_147386_b.getNetHandler()).hostname = p_147383_1_.field_149598_b + ":" + p_147383_1_.field_149599_c; // CraftBukkit - set hostname + } + + break; diff --git a/patches/net/minecraft/server/network/NetHandlerLoginServer.java.patch b/patches/net/minecraft/server/network/NetHandlerLoginServer.java.patch new file mode 100644 index 0000000..115567e --- /dev/null +++ b/patches/net/minecraft/server/network/NetHandlerLoginServer.java.patch @@ -0,0 +1,239 @@ +--- ../src-base/minecraft/net/minecraft/server/network/NetHandlerLoginServer.java ++++ ../src-work/minecraft/net/minecraft/server/network/NetHandlerLoginServer.java +@@ -13,6 +13,7 @@ + import java.util.UUID; + import java.util.concurrent.atomic.AtomicInteger; + import javax.crypto.SecretKey; ++ + import net.minecraft.network.EnumConnectionState; + import net.minecraft.network.NetworkManager; + import net.minecraft.network.login.INetHandlerLoginServer; +@@ -29,6 +30,14 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import org.bukkit.craftbukkit.util.Waitable; ++import org.bukkit.event.player.AsyncPlayerPreLoginEvent; ++import org.bukkit.event.player.PlayerPreLoginEvent; ++// CraftBukkit end ++import com.mojang.authlib.properties.Property; ++ + public class NetHandlerLoginServer implements INetHandlerLoginServer + { + private static final AtomicInteger field_147331_b = new AtomicInteger(0); +@@ -37,16 +46,17 @@ + private final byte[] field_147330_e = new byte[4]; + private final MinecraftServer field_147327_f; + public final NetworkManager field_147333_a; +- private NetHandlerLoginServer.LoginState field_147328_g; ++ private LoginState field_147328_g; + private int field_147336_h; + private GameProfile field_147337_i; + private String field_147334_j; + private SecretKey field_147335_k; ++ public String hostname = ""; // CraftBukkit - add field + private static final String __OBFID = "CL_00001458"; + + public NetHandlerLoginServer(MinecraftServer p_i45298_1_, NetworkManager p_i45298_2_) + { +- this.field_147328_g = NetHandlerLoginServer.LoginState.HELLO; ++ this.field_147328_g = LoginState.HELLO; + this.field_147334_j = ""; + this.field_147327_f = p_i45298_1_; + this.field_147333_a = p_i45298_2_; +@@ -55,7 +65,7 @@ + + public void onNetworkTick() + { +- if (this.field_147328_g == NetHandlerLoginServer.LoginState.READY_TO_ACCEPT) ++ if (this.field_147328_g == LoginState.READY_TO_ACCEPT) + { + this.func_147326_c(); + } +@@ -81,24 +91,54 @@ + } + } + ++ // Spigot start ++ public void initUUID() ++ { ++ UUID uuid; ++ if ( field_147333_a.spoofedUUID != null ) ++ { ++ uuid = field_147333_a.spoofedUUID; ++ } else ++ { ++ uuid = UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + this.field_147337_i.getName() ).getBytes( Charsets.UTF_8 ) ); ++ } ++ ++ this.field_147337_i = new GameProfile( uuid, this.field_147337_i.getName() ); ++ ++ if (field_147333_a.spoofedProfile != null) ++ { ++ for ( Property property : field_147333_a.spoofedProfile ) ++ { ++ this.field_147337_i.getProperties().put( property.getName(), property ); ++ } ++ } ++ } ++ // Spigot end ++ + public void func_147326_c() + { ++ // Spigot start - Moved to initUUID ++ /* + if (!this.field_147337_i.isComplete()) + { + this.field_147337_i = this.func_152506_a(this.field_147337_i); + } ++ */ ++ // Spigot end + +- String s = this.field_147327_f.getConfigurationManager().allowUserToConnect(this.field_147333_a.getSocketAddress(), this.field_147337_i); ++ // CraftBukkit start - fire PlayerLoginEvent ++ EntityPlayerMP s = this.field_147327_f.getConfigurationManager().attemptLogin(this, this.field_147337_i, this.hostname); + +- if (s != null) ++ if (s == null) + { +- this.func_147322_a(s); ++ // this.func_147322_a(s); ++ // CraftBukkit end + } + else + { + this.field_147328_g = NetHandlerLoginServer.LoginState.ACCEPTED; + this.field_147333_a.scheduleOutboundPacket(new S02PacketLoginSuccess(this.field_147337_i), new GenericFutureListener[0]); +- FMLNetworkHandler.fmlServerHandshake(this.field_147327_f.getConfigurationManager(), this.field_147333_a, this.field_147327_f.getConfigurationManager().createPlayerForUser(this.field_147337_i)); ++ FMLNetworkHandler.fmlServerHandshake(this.field_147327_f.getConfigurationManager(), this.field_147333_a, this.field_147327_f.getConfigurationManager().processLogin(this.field_147337_i, s)); // CraftBukkit - add player reference + } + } + +@@ -114,29 +154,29 @@ + + public void onConnectionStateTransition(EnumConnectionState p_147232_1_, EnumConnectionState p_147232_2_) + { +- Validate.validState(this.field_147328_g == NetHandlerLoginServer.LoginState.ACCEPTED || this.field_147328_g == NetHandlerLoginServer.LoginState.HELLO, "Unexpected change in protocol", new Object[0]); ++ Validate.validState(this.field_147328_g == LoginState.ACCEPTED || this.field_147328_g == LoginState.HELLO, "Unexpected change in protocol", new Object[0]); + Validate.validState(p_147232_2_ == EnumConnectionState.PLAY || p_147232_2_ == EnumConnectionState.LOGIN, "Unexpected protocol " + p_147232_2_, new Object[0]); + } + + public void processLoginStart(C00PacketLoginStart p_147316_1_) + { +- Validate.validState(this.field_147328_g == NetHandlerLoginServer.LoginState.HELLO, "Unexpected hello packet", new Object[0]); ++ Validate.validState(this.field_147328_g == LoginState.HELLO, "Unexpected hello packet", new Object[0]); + this.field_147337_i = p_147316_1_.func_149304_c(); + + if (this.field_147327_f.isServerInOnlineMode() && !this.field_147333_a.isLocalChannel()) + { +- this.field_147328_g = NetHandlerLoginServer.LoginState.KEY; ++ this.field_147328_g = LoginState.KEY; + this.field_147333_a.scheduleOutboundPacket(new S01PacketEncryptionRequest(this.field_147334_j, this.field_147327_f.getKeyPair().getPublic(), this.field_147330_e), new GenericFutureListener[0]); + } + else + { +- this.field_147328_g = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; ++ (new ThreadPlayerLookupUUID(this, "User Authenticator #" + field_147331_b.incrementAndGet())).start(); // Spigot + } + } + + public void processEncryptionResponse(C01PacketEncryptionResponse p_147315_1_) + { +- Validate.validState(this.field_147328_g == NetHandlerLoginServer.LoginState.KEY, "Unexpected key packet", new Object[0]); ++ Validate.validState(this.field_147328_g == LoginState.KEY, "Unexpected key packet", new Object[0]); + PrivateKey privatekey = this.field_147327_f.getKeyPair().getPrivate(); + + if (!Arrays.equals(this.field_147330_e, p_147315_1_.func_149299_b(privatekey))) +@@ -148,51 +188,7 @@ + this.field_147335_k = p_147315_1_.func_149300_a(privatekey); + this.field_147328_g = NetHandlerLoginServer.LoginState.AUTHENTICATING; + this.field_147333_a.enableEncryption(this.field_147335_k); +- (new Thread("User Authenticator #" + field_147331_b.incrementAndGet()) +- { +- private static final String __OBFID = "CL_00001459"; +- public void run() +- { +- GameProfile gameprofile = NetHandlerLoginServer.this.field_147337_i; +- +- try +- { +- String s = (new BigInteger(CryptManager.getServerIdHash(NetHandlerLoginServer.this.field_147334_j, NetHandlerLoginServer.this.field_147327_f.getKeyPair().getPublic(), NetHandlerLoginServer.this.field_147335_k))).toString(16); +- NetHandlerLoginServer.this.field_147337_i = NetHandlerLoginServer.this.field_147327_f.func_147130_as().hasJoinedServer(new GameProfile((UUID)null, gameprofile.getName()), s); +- +- if (NetHandlerLoginServer.this.field_147337_i != null) +- { +- NetHandlerLoginServer.logger.info("UUID of player " + NetHandlerLoginServer.this.field_147337_i.getName() + " is " + NetHandlerLoginServer.this.field_147337_i.getId()); +- NetHandlerLoginServer.this.field_147328_g = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; +- } +- else if (NetHandlerLoginServer.this.field_147327_f.isSinglePlayer()) +- { +- NetHandlerLoginServer.logger.warn("Failed to verify username but will let them in anyway!"); +- NetHandlerLoginServer.this.field_147337_i = NetHandlerLoginServer.this.func_152506_a(gameprofile); +- NetHandlerLoginServer.this.field_147328_g = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; +- } +- else +- { +- NetHandlerLoginServer.this.func_147322_a("Failed to verify username!"); +- NetHandlerLoginServer.logger.error("Username \'" + NetHandlerLoginServer.this.field_147337_i.getName() + "\' tried to join with an invalid session"); +- } +- } +- catch (AuthenticationUnavailableException authenticationunavailableexception) +- { +- if (NetHandlerLoginServer.this.field_147327_f.isSinglePlayer()) +- { +- NetHandlerLoginServer.logger.warn("Authentication servers are down but will let them in anyway!"); +- NetHandlerLoginServer.this.field_147337_i = NetHandlerLoginServer.this.func_152506_a(gameprofile); +- NetHandlerLoginServer.this.field_147328_g = NetHandlerLoginServer.LoginState.READY_TO_ACCEPT; +- } +- else +- { +- NetHandlerLoginServer.this.func_147322_a("Authentication servers are down. Please try again later, sorry!"); +- NetHandlerLoginServer.logger.error("Couldn\'t verify username because servers are unavailable"); +- } +- } +- } +- }).start(); ++ (new ThreadPlayerLookupUUID(this, "User Authenticator #" + field_147331_b.incrementAndGet())).start(); + } + } + +@@ -202,6 +198,37 @@ + return new GameProfile(uuid, p_152506_1_.getName()); + } + ++ // Cauldron start - access methods for ThreadPlayerLookupUUID ++ static String getLoginServerId(NetHandlerLoginServer loginServer) { ++ return loginServer.field_147334_j; ++ } ++ ++ static MinecraftServer getMinecraftServer(NetHandlerLoginServer loginServer) { ++ return loginServer.field_147327_f; ++ } ++ ++ static SecretKey getSecretKey(NetHandlerLoginServer loginServer) { ++ return loginServer.field_147335_k; ++ } ++ ++ static GameProfile processPlayerLoginGameProfile(NetHandlerLoginServer loginServer, GameProfile gameprofile) { ++ return loginServer.field_147337_i = gameprofile; ++ } ++ ++ static GameProfile getGameProfile(NetHandlerLoginServer loginServer) { ++ return loginServer.field_147337_i; ++ } ++ ++ static Logger getLogger() { ++ return logger; ++ } ++ ++ static void setLoginState(NetHandlerLoginServer loginServer, LoginState state) ++ { ++ loginServer.field_147328_g = state; ++ } ++ // Cauldron end ++ + static enum LoginState + { + HELLO, diff --git a/patches/net/minecraft/server/network/NetHandlerStatusServer.java.patch b/patches/net/minecraft/server/network/NetHandlerStatusServer.java.patch new file mode 100644 index 0000000..885e48b --- /dev/null +++ b/patches/net/minecraft/server/network/NetHandlerStatusServer.java.patch @@ -0,0 +1,61 @@ +--- ../src-base/minecraft/net/minecraft/server/network/NetHandlerStatusServer.java ++++ ../src-work/minecraft/net/minecraft/server/network/NetHandlerStatusServer.java +@@ -1,5 +1,6 @@ + package net.minecraft.server.network; + ++ + import io.netty.util.concurrent.GenericFutureListener; + import net.minecraft.network.EnumConnectionState; + import net.minecraft.network.NetworkManager; +@@ -11,6 +12,13 @@ + import net.minecraft.server.MinecraftServer; + import net.minecraft.util.IChatComponent; + ++// CraftBukkit start ++import java.net.InetSocketAddress; ++import net.minecraft.network.ServerStatusResponse; ++import net.minecraft.util.ChatComponentText; ++import org.bukkit.craftbukkit.util.CraftIconCache; ++// CraftBukkit end ++ + public class NetHandlerStatusServer implements INetHandlerStatusServer + { + private final MinecraftServer field_147314_a; +@@ -37,7 +45,36 @@ + + public void processServerQuery(C00PacketServerQuery p_147312_1_) + { +- this.field_147313_b.scheduleOutboundPacket(new S00PacketServerInfo(this.field_147314_a.func_147134_at()), new GenericFutureListener[0]); ++ // CraftBukkit start - fire ping event ++ class ServerListPingEvent extends org.bukkit.event.server.ServerListPingEvent ++ { ++ CraftIconCache icon = field_147314_a.server.getServerIcon(); ++ ++ ServerListPingEvent() ++ { ++ super(((InetSocketAddress) field_147313_b.getSocketAddress()).getAddress(), field_147314_a.getMOTD(), field_147314_a.getConfigurationManager().getCurrentPlayerCount(), field_147314_a.getConfigurationManager().getMaxPlayers()); ++ } ++ ++ @Override ++ public void setServerIcon(org.bukkit.util.CachedServerIcon icon) ++ { ++ if (!(icon instanceof CraftIconCache)) ++ { ++ throw new IllegalArgumentException(icon + " was not created by " + org.bukkit.craftbukkit.CraftServer.class); ++ } ++ ++ this.icon = (CraftIconCache) icon; ++ } ++ } ++ ServerListPingEvent event = new ServerListPingEvent(); ++ this.field_147314_a.server.getPluginManager().callEvent(event); ++ ServerStatusResponse ping = new ServerStatusResponse(); ++ ping.func_151320_a(event.icon.value); ++ ping.func_151315_a(new ChatComponentText(event.getMotd())); ++ ping.func_151319_a(new ServerStatusResponse.PlayerCountData(event.getMaxPlayers(), field_147314_a.getConfigurationManager().getCurrentPlayerCount())); ++ ping.func_151321_a(new ServerStatusResponse.MinecraftProtocolVersionIdentifier(field_147314_a.getServerModName() + " " + field_147314_a.getMinecraftVersion(), 5)); // TODO: Update when protocol changes ++ this.field_147313_b.scheduleOutboundPacket(new S00PacketServerInfo(ping), new GenericFutureListener[0]); ++ // CraftBukkit end + } + + public void processPing(C01PacketPing p_147311_1_) diff --git a/patches/net/minecraft/stats/StatFileWriter.java.patch b/patches/net/minecraft/stats/StatFileWriter.java.patch new file mode 100644 index 0000000..af15939 --- /dev/null +++ b/patches/net/minecraft/stats/StatFileWriter.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/stats/StatFileWriter.java ++++ ../src-work/minecraft/net/minecraft/stats/StatFileWriter.java +@@ -27,6 +27,12 @@ + { + if (!p_150871_2_.isAchievement() || this.canUnlockAchievement((Achievement)p_150871_2_)) + { ++ // CraftBukkit start ++ org.bukkit.event.Cancellable cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.handleStatisticsIncrease(p_150871_1_, p_150871_2_, this.writeStat(p_150871_2_), p_150871_3_); ++ if (cancellable != null && cancellable.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + this.func_150873_a(p_150871_1_, p_150871_2_, this.writeStat(p_150871_2_) + p_150871_3_); + } + } diff --git a/patches/net/minecraft/tileentity/MobSpawnerBaseLogic.java.patch b/patches/net/minecraft/tileentity/MobSpawnerBaseLogic.java.patch new file mode 100644 index 0000000..0964ba4 --- /dev/null +++ b/patches/net/minecraft/tileentity/MobSpawnerBaseLogic.java.patch @@ -0,0 +1,46 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.java ++++ ../src-work/minecraft/net/minecraft/tileentity/MobSpawnerBaseLogic.java +@@ -17,6 +17,8 @@ + import net.minecraft.util.WeightedRandom; + import net.minecraft.world.World; + ++import org.bukkit.event.entity.CreatureSpawnEvent; // CraftBukkit ++ + public abstract class MobSpawnerBaseLogic + { + public int spawnDelay = 20; +@@ -162,7 +164,15 @@ + + if (p_98265_1_.worldObj != null) + { +- p_98265_1_.worldObj.spawnEntityInWorld(p_98265_1_); ++ p_98265_1_.worldObj.addEntity(p_98265_1_, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit ++ ++ // Spigot Start ++ if (p_98265_1_.worldObj.getSpigotConfig().nerfSpawnerMobs) // Cauldron ++ { ++ p_98265_1_.fromMobSpawner = true; ++ } ++ ++ // Spigot End + } + + NBTTagCompound nbttagcompound2; +@@ -190,7 +200,7 @@ + + if (p_98265_1_.worldObj != null) + { +- p_98265_1_.worldObj.spawnEntityInWorld(entity2); ++ p_98265_1_.worldObj.addEntity(entity2, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + } + + entity1.mountEntity(entity2); +@@ -202,7 +212,7 @@ + else if (p_98265_1_ instanceof EntityLivingBase && p_98265_1_.worldObj != null) + { + ((EntityLiving)p_98265_1_).onSpawnWithEgg((IEntityLivingData)null); +- this.getSpawnerWorld().spawnEntityInWorld(p_98265_1_); ++ this.getSpawnerWorld().addEntity(p_98265_1_, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + } + + return p_98265_1_; diff --git a/patches/net/minecraft/tileentity/TileEntity.java.patch b/patches/net/minecraft/tileentity/TileEntity.java.patch new file mode 100644 index 0000000..8b36b9b --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntity.java.patch @@ -0,0 +1,61 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntity.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntity.java +@@ -22,18 +22,22 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import org.spigotmc.CustomTimingsHandler; // Spigot ++import org.bukkit.inventory.InventoryHolder; // CraftBukkit ++ + public class TileEntity + { + private static final Logger logger = LogManager.getLogger(); + private static Map nameToClassMap = new HashMap(); +- private static Map classToNameMap = new HashMap(); +- protected World worldObj; ++ public static Map classToNameMap = new HashMap(); // Cauldron - private -> public ++ public World worldObj; // CraftBukkit - protected -> public + public int xCoord; + public int yCoord; + public int zCoord; + protected boolean tileEntityInvalid; + public int blockMetadata = -1; + public Block blockType; ++ public CustomTimingsHandler tickTimer = org.bukkit.craftbukkit.SpigotTimings.getTileEntityTimings(this); // Spigot + private static final String __OBFID = "CL_00000340"; + + public static void addMapping(Class p_145826_0_, String p_145826_1_) +@@ -106,7 +110,11 @@ + } + catch (Exception exception) + { +- exception.printStackTrace(); ++ // Cauldron start - better debug ++ FMLLog.log(Level.ERROR, exception, ++ "A TileEntity %s(%s) located @ %s,%s,%s has thrown an exception during creation, it cannot be created. Report this to the mod author", ++ p_145827_0_.getString("id"), oclass.getName(), p_145827_0_.getInteger("x"), p_145827_0_.getInteger("y"), p_145827_0_.getInteger("z")); ++ // Cauldron end + } + + if (tileentity != null) +@@ -282,6 +290,20 @@ + addMapping(TileEntityFlowerPot.class, "FlowerPot"); + } + ++ // CraftBukkit start ++ public InventoryHolder getOwner() ++ { ++ org.bukkit.block.BlockState state = worldObj.getWorld().getBlockAt(xCoord, yCoord, zCoord).getState(); ++ ++ if (state instanceof InventoryHolder) ++ { ++ return (InventoryHolder) state; ++ } ++ ++ return null; ++ } ++ // CraftBukkit end ++ + // -- BEGIN FORGE PATCHES -- + /** + * Determines if this TileEntity requires update calls. diff --git a/patches/net/minecraft/tileentity/TileEntityBeacon.java.patch b/patches/net/minecraft/tileentity/TileEntityBeacon.java.patch new file mode 100644 index 0000000..ca827c1 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityBeacon.java.patch @@ -0,0 +1,59 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityBeacon.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityBeacon.java +@@ -18,6 +18,11 @@ + import net.minecraft.stats.AchievementList; + import net.minecraft.util.AxisAlignedBB; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class TileEntityBeacon extends TileEntity implements IInventory + { + public static final Potion[][] effectsList = new Potion[][] {{Potion.moveSpeed, Potion.digSpeed}, {Potion.resistance, Potion.jump}, {Potion.damageBoost}, {Potion.regeneration}}; +@@ -31,6 +36,35 @@ + private int secondaryEffect; + private ItemStack payment; + private String field_146008_p; ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return new ItemStack[] { this.payment }; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00000339"; + + public void updateEntity() +@@ -343,7 +377,7 @@ + + public int getInventoryStackLimit() + { +- return 1; ++ return maxStack; // CraftBukkit + } + + public boolean isUseableByPlayer(EntityPlayer p_70300_1_) diff --git a/patches/net/minecraft/tileentity/TileEntityBrewingStand.java.patch b/patches/net/minecraft/tileentity/TileEntityBrewingStand.java.patch new file mode 100644 index 0000000..0a5fff5 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityBrewingStand.java.patch @@ -0,0 +1,109 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityBrewingStand.java +@@ -13,17 +13,55 @@ + import net.minecraft.nbt.NBTTagList; + import net.minecraft.potion.PotionHelper; + ++// CraftBukkit start ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.event.inventory.BrewEvent; ++// CraftBukkit end ++ + public class TileEntityBrewingStand extends TileEntity implements ISidedInventory + { + private static final int[] field_145941_a = new int[] {3}; + private static final int[] field_145947_i = new int[] {0, 1, 2}; +- private ItemStack[] brewingItemStacks = new ItemStack[4]; +- private int brewTime; ++ public ItemStack[] brewingItemStacks = new ItemStack[4]; // CraftBukkit - private -> public ++ public int brewTime; // CraftBukkit - private -> public + private int filledSlots; + private Item ingredientID; + private String field_145942_n; ++ private int lastTick = MinecraftServer.currentTick; // CraftBukkit + private static final String __OBFID = "CL_00000345"; + ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private int maxStack = 64; ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public ItemStack[] getContents() ++ { ++ return this.brewingItemStacks; ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end ++ + public String getInventoryName() + { + return this.hasCustomInventoryName() ? this.field_145942_n : "container.brewing"; +@@ -46,12 +84,17 @@ + + public void updateEntity() + { ++ // CraftBukkit start - Use wall time instead of ticks for brewing ++ int elapsedTicks = MinecraftServer.currentTick - this.lastTick; ++ this.lastTick = MinecraftServer.currentTick; ++ + if (this.brewTime > 0) + { +- --this.brewTime; ++ this.brewTime -= elapsedTicks; + +- if (this.brewTime == 0) ++ if (this.brewTime <= 0) // == -> <= + { ++ // CraftBukkit end + this.brewPotions(); + this.markDirty(); + } +@@ -141,7 +184,19 @@ + if (this.canBrew()) + { + ItemStack itemstack = this.brewingItemStacks[3]; ++ // CraftBukkit start ++ if (getOwner() != null) ++ { ++ BrewEvent event = new BrewEvent(worldObj.getWorld().getBlockAt(xCoord, yCoord, zCoord), (org.bukkit.inventory.BrewerInventory) this.getOwner() ++ .getInventory()); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); + ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ } ++ // CraftBukkit end + for (int i = 0; i < 3; ++i) + { + if (this.brewingItemStacks[i] != null && this.brewingItemStacks[i].getItem() instanceof ItemPotion) +@@ -280,7 +335,7 @@ + + public int getInventoryStackLimit() + { +- return 64; ++ return this.maxStack; // CraftBukkit + } + + public boolean isUseableByPlayer(EntityPlayer p_70300_1_) diff --git a/patches/net/minecraft/tileentity/TileEntityChest.java.patch b/patches/net/minecraft/tileentity/TileEntityChest.java.patch new file mode 100644 index 0000000..fbeb9dc --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityChest.java.patch @@ -0,0 +1,123 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityChest.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityChest.java +@@ -15,6 +15,12 @@ + import net.minecraft.nbt.NBTTagList; + import net.minecraft.util.AxisAlignedBB; + ++// CraftBukkit start ++import net.minecraft.init.Blocks; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class TileEntityChest extends TileEntity implements IInventory + { + private ItemStack[] chestContents = new ItemStack[36]; +@@ -31,6 +37,36 @@ + private String customName; + private static final String __OBFID = "CL_00000346"; + ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return this.chestContents; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end ++ + public TileEntityChest() + { + this.cachedChestType = -1; +@@ -296,6 +332,12 @@ + public void updateEntity() + { + super.updateEntity(); ++ ++ if (this.worldObj == null) ++ { ++ return; // CraftBukkit ++ } ++ + this.checkForAdjacentChests(); + ++this.ticksSinceSync; + float f; +@@ -410,8 +452,28 @@ + this.numPlayersUsing = 0; + } + ++ int oldPower = Math.max(0, Math.min(15, this.numPlayersUsing)); // CraftBukkit - Get power before new viewer is added + ++this.numPlayersUsing; ++ ++ if (this.worldObj == null) ++ { ++ return; // CraftBukkit ++ } ++ + this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType(), 1, this.numPlayersUsing); ++ ++ // CraftBukkit start - Call redstone event ++ if (this.getBlockType() == Blocks.trapped_chest) ++ { ++ int newPower = Math.max(0, Math.min(15, this.numPlayersUsing)); ++ ++ if (oldPower != newPower) ++ { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(worldObj, this.xCoord, this.yCoord, this.zCoord, oldPower, newPower); ++ } ++ } ++ ++ // CraftBukkit end + this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord, this.zCoord, this.getBlockType()); + this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord - 1, this.zCoord, this.getBlockType()); + } +@@ -420,8 +482,28 @@ + { + if (this.getBlockType() instanceof BlockChest) + { ++ int oldPower = Math.max(0, Math.min(15, this.numPlayersUsing)); // CraftBukkit - Get power before new viewer is added + --this.numPlayersUsing; ++ ++ if (this.worldObj == null) ++ { ++ return; // CraftBukkit ++ } ++ + this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType(), 1, this.numPlayersUsing); ++ ++ // CraftBukkit start - Call redstone event ++ if (this.getBlockType() == Blocks.trapped_chest) ++ { ++ int newPower = Math.max(0, Math.min(15, this.numPlayersUsing)); ++ ++ if (oldPower != newPower) ++ { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(worldObj, this.xCoord, this.yCoord, this.zCoord, oldPower, newPower); ++ } ++ } ++ ++ // CraftBukkit end + this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord, this.zCoord, this.getBlockType()); + this.worldObj.notifyBlocksOfNeighborChange(this.xCoord, this.yCoord - 1, this.zCoord, this.getBlockType()); + } diff --git a/patches/net/minecraft/tileentity/TileEntityCommandBlock.java.patch b/patches/net/minecraft/tileentity/TileEntityCommandBlock.java.patch new file mode 100644 index 0000000..764cf12 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityCommandBlock.java.patch @@ -0,0 +1,58 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityCommandBlock.java +@@ -12,39 +12,9 @@ + + public class TileEntityCommandBlock extends TileEntity + { +- private final CommandBlockLogic field_145994_a = new CommandBlockLogic() +- { +- private static final String __OBFID = "CL_00000348"; +- public ChunkCoordinates getPlayerCoordinates() +- { +- return new ChunkCoordinates(TileEntityCommandBlock.this.xCoord, TileEntityCommandBlock.this.yCoord, TileEntityCommandBlock.this.zCoord); +- } +- public World getEntityWorld() +- { +- return TileEntityCommandBlock.this.getWorldObj(); +- } +- public void func_145752_a(String p_145752_1_) +- { +- super.func_145752_a(p_145752_1_); +- TileEntityCommandBlock.this.markDirty(); +- } +- public void func_145756_e() +- { +- TileEntityCommandBlock.this.getWorldObj().markBlockForUpdate(TileEntityCommandBlock.this.xCoord, TileEntityCommandBlock.this.yCoord, TileEntityCommandBlock.this.zCoord); +- } +- @SideOnly(Side.CLIENT) +- public int func_145751_f() +- { +- return 0; +- } +- @SideOnly(Side.CLIENT) +- public void func_145757_a(ByteBuf p_145757_1_) +- { +- p_145757_1_.writeInt(TileEntityCommandBlock.this.xCoord); +- p_145757_1_.writeInt(TileEntityCommandBlock.this.yCoord); +- p_145757_1_.writeInt(TileEntityCommandBlock.this.zCoord); +- } +- }; ++ private final TileEntityCommandBlockListener field_145994_a_CB = new TileEntityCommandBlockListener(this); // CraftBukkit ++ private final CommandBlockLogic field_145994_a = field_145994_a_CB; // Cauldron ++ + private static final String __OBFID = "CL_00000347"; + + public void writeToNBT(NBTTagCompound p_145841_1_) +@@ -70,4 +40,12 @@ + { + return this.field_145994_a; + } ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/tileentity/TileEntityComparator.java.patch b/patches/net/minecraft/tileentity/TileEntityComparator.java.patch new file mode 100644 index 0000000..aaae5fa --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityComparator.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityComparator.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityComparator.java +@@ -28,4 +28,12 @@ + { + this.field_145997_a = p_145995_1_; + } ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/tileentity/TileEntityDispenser.java.patch b/patches/net/minecraft/tileentity/TileEntityDispenser.java.patch new file mode 100644 index 0000000..1809089 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityDispenser.java.patch @@ -0,0 +1,87 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityDispenser.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityDispenser.java +@@ -7,6 +7,13 @@ + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.nbt.NBTTagList; + ++// CraftBukkit start ++import java.util.List; ++ ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class TileEntityDispenser extends TileEntity implements IInventory + { + private ItemStack[] field_146022_i = new ItemStack[9]; +@@ -14,6 +21,36 @@ + protected String field_146020_a; + private static final String __OBFID = "CL_00000352"; + ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return this.field_146022_i; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end ++ + public int getSizeInventory() + { + return 9; +@@ -79,6 +116,11 @@ + { + if (this.field_146022_i[k] != null && this.field_146021_j.nextInt(j++) == 0) + { ++ if (this.field_146022_i[k].stackSize == 0) ++ { ++ continue; // CraftBukkit ++ } ++ + i = k; + } + } +@@ -176,7 +218,7 @@ + + public int getInventoryStackLimit() + { +- return 64; ++ return maxStack; // CraftBukkit + } + + public boolean isUseableByPlayer(EntityPlayer p_70300_1_) +@@ -192,4 +234,12 @@ + { + return true; + } ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/tileentity/TileEntityDropper.java.patch b/patches/net/minecraft/tileentity/TileEntityDropper.java.patch new file mode 100644 index 0000000..5e97e9a --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityDropper.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityDropper.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityDropper.java +@@ -8,4 +8,12 @@ + { + return this.hasCustomInventoryName() ? this.field_146020_a : "container.dropper"; + } ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/tileentity/TileEntityEndPortal.java.patch b/patches/net/minecraft/tileentity/TileEntityEndPortal.java.patch new file mode 100644 index 0000000..fa47fd5 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityEndPortal.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityEndPortal.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityEndPortal.java +@@ -3,4 +3,12 @@ + public class TileEntityEndPortal extends TileEntity + { + private static final String __OBFID = "CL_00000365"; ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/tileentity/TileEntityFlowerPot.java.patch b/patches/net/minecraft/tileentity/TileEntityFlowerPot.java.patch new file mode 100644 index 0000000..f6dc35e --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityFlowerPot.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityFlowerPot.java +@@ -55,4 +55,12 @@ + { + return this.flowerPotData; + } ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/tileentity/TileEntityFurnace.java.patch b/patches/net/minecraft/tileentity/TileEntityFurnace.java.patch new file mode 100644 index 0000000..e3390fd --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityFurnace.java.patch @@ -0,0 +1,219 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityFurnace.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityFurnace.java +@@ -19,7 +19,17 @@ + import net.minecraft.item.crafting.FurnaceRecipes; + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.nbt.NBTTagList; ++import net.minecraft.server.MinecraftServer; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.event.inventory.FurnaceBurnEvent; ++import org.bukkit.event.inventory.FurnaceSmeltEvent; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++// CraftBukkit end ++ + public class TileEntityFurnace extends TileEntity implements ISidedInventory + { + private static final int[] slotsTop = new int[] {0}; +@@ -30,6 +40,37 @@ + public int currentItemBurnTime; + public int furnaceCookTime; + private String field_145958_o; ++ ++ // CraftBukkit start ++ private int lastTick = MinecraftServer.currentTick; ++ private int maxStack = MAX_STACK; ++ public List transaction = new java.util.ArrayList(); ++ ++ public ItemStack[] getContents() ++ { ++ return this.furnaceItemStacks; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00000357"; + + public int getSizeInventory() +@@ -166,7 +207,7 @@ + + public int getInventoryStackLimit() + { +- return 64; ++ return maxStack; // CraftBukkit + } + + @SideOnly(Side.CLIENT) +@@ -195,52 +236,85 @@ + { + boolean flag = this.furnaceBurnTime > 0; + boolean flag1 = false; ++ // CraftBukkit start - Use wall time instead of ticks for cooking ++ int elapsedTicks = MinecraftServer.currentTick - this.lastTick; ++ this.lastTick = MinecraftServer.currentTick; + ++ // CraftBukkit - moved from below ++ if (this.isBurning() && this.canSmelt()) ++ { ++ this.furnaceCookTime += elapsedTicks; ++ ++ if (this.furnaceCookTime >= 200) ++ { ++ this.furnaceCookTime %= 200; ++ this.smeltItem(); ++ flag1 = true; ++ } ++ } ++ else ++ { ++ this.furnaceCookTime = 0; ++ } ++ ++ // CraftBukkit end ++ + if (this.furnaceBurnTime > 0) + { +- --this.furnaceBurnTime; ++ this.furnaceBurnTime -= elapsedTicks; // CraftBukkit + } + + if (!this.worldObj.isRemote) + { +- if (this.furnaceBurnTime != 0 || this.furnaceItemStacks[1] != null && this.furnaceItemStacks[0] != null) ++ // CraftBukkit start - Handle multiple elapsed ticks ++ if (this.furnaceBurnTime <= 0 && this.canSmelt() && this.furnaceItemStacks[1] != null) // CraftBukkit - == to <= + { +- if (this.furnaceBurnTime == 0 && this.canSmelt()) ++ CraftItemStack fuel = CraftItemStack.asCraftMirror(this.furnaceItemStacks[1]); ++ FurnaceBurnEvent furnaceBurnEvent = new FurnaceBurnEvent(this.worldObj.getWorld().getBlockAt(this.xCoord, this.yCoord, this.zCoord), fuel, getItemBurnTime(this.furnaceItemStacks[1])); ++ this.worldObj.getServer().getPluginManager().callEvent(furnaceBurnEvent); ++ ++ if (furnaceBurnEvent.isCancelled()) + { +- this.currentItemBurnTime = this.furnaceBurnTime = getItemBurnTime(this.furnaceItemStacks[1]); ++ return; ++ } + +- if (this.furnaceBurnTime > 0) ++ this.currentItemBurnTime = furnaceBurnEvent.getBurnTime(); ++ this.furnaceBurnTime += this.currentItemBurnTime; ++ ++ if (this.furnaceBurnTime > 0 && furnaceBurnEvent.isBurning()) ++ { ++ // CraftBukkit end ++ flag1 = true; ++ ++ if (this.furnaceItemStacks[1] != null) + { +- flag1 = true; ++ --this.furnaceItemStacks[1].stackSize; + +- if (this.furnaceItemStacks[1] != null) ++ if (this.furnaceItemStacks[1].stackSize == 0) + { +- --this.furnaceItemStacks[1].stackSize; +- +- if (this.furnaceItemStacks[1].stackSize == 0) +- { +- this.furnaceItemStacks[1] = furnaceItemStacks[1].getItem().getContainerItem(furnaceItemStacks[1]); +- } ++ this.furnaceItemStacks[1] = furnaceItemStacks[1].getItem().getContainerItem(furnaceItemStacks[1]); + } + } + } ++ } + +- if (this.isBurning() && this.canSmelt()) +- { +- ++this.furnaceCookTime; ++ /* CraftBukkit start - Moved up ++ if (this.isBurning() && this.canSmelt()) ++ { ++ ++this.furnaceCookTime; + +- if (this.furnaceCookTime == 200) +- { +- this.furnaceCookTime = 0; +- this.smeltItem(); +- flag1 = true; +- } +- } +- else ++ if (this.furnaceCookTime == 200) + { + this.furnaceCookTime = 0; ++ this.smeltItem(); ++ flag1 = true; + } + } ++ else ++ { ++ this.furnaceCookTime = 0; ++ } ++ // CraftBukkit end */ + + if (flag != this.furnaceBurnTime > 0) + { +@@ -277,16 +351,37 @@ + if (this.canSmelt()) + { + ItemStack itemstack = FurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]); ++ // CraftBukkit start ++ CraftItemStack source = CraftItemStack.asCraftMirror(this.furnaceItemStacks[0]); ++ org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack); ++ FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(this.worldObj.getWorld().getBlockAt(this.xCoord, this.yCoord, this.zCoord), source, result); ++ this.worldObj.getServer().getPluginManager().callEvent(furnaceSmeltEvent); + +- if (this.furnaceItemStacks[2] == null) ++ if (furnaceSmeltEvent.isCancelled()) + { +- this.furnaceItemStacks[2] = itemstack.copy(); ++ return; + } +- else if (this.furnaceItemStacks[2].getItem() == itemstack.getItem()) ++ ++ result = furnaceSmeltEvent.getResult(); ++ itemstack = CraftItemStack.asNMSCopy(result); ++ ++ if (itemstack != null) + { +- this.furnaceItemStacks[2].stackSize += itemstack.stackSize; // Forge BugFix: Results may have multiple items ++ if (this.furnaceItemStacks[2] == null) ++ { ++ this.furnaceItemStacks[2] = itemstack; ++ } ++ else if (CraftItemStack.asCraftMirror(this.furnaceItemStacks[2]).isSimilar(result)) ++ { ++ this.furnaceItemStacks[2].stackSize += itemstack.stackSize; ++ } ++ else ++ { ++ return; ++ } + } + ++ // CraftBukkit end + --this.furnaceItemStacks[0].stackSize; + + if (this.furnaceItemStacks[0].stackSize <= 0) diff --git a/patches/net/minecraft/tileentity/TileEntityHopper.java.patch b/patches/net/minecraft/tileentity/TileEntityHopper.java.patch new file mode 100644 index 0000000..8186774 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityHopper.java.patch @@ -0,0 +1,245 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityHopper.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityHopper.java +@@ -18,11 +18,52 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.world.World; + ++// CraftBukkit start ++import net.minecraft.entity.item.EntityMinecartHopper; ++import net.minecraft.inventory.InventoryLargeChest; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.event.inventory.InventoryMoveItemEvent; ++import org.bukkit.event.inventory.InventoryPickupItemEvent; ++import org.bukkit.inventory.Inventory; ++// CraftBukkit end ++ + public class TileEntityHopper extends TileEntity implements IHopper + { + private ItemStack[] field_145900_a = new ItemStack[5]; + private String field_145902_i; + private int field_145901_j = -1; ++ ++ // CraftBukkit start ++ public List transaction = new java.util.ArrayList(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() ++ { ++ return this.field_145900_a; ++ } ++ ++ public void onOpen(CraftHumanEntity who) ++ { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) ++ { ++ transaction.remove(who); ++ } ++ ++ public List getViewers() ++ { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) ++ { ++ maxStack = size; ++ } ++ // CraftBukkit end + private static final String __OBFID = "CL_00000359"; + + public void readFromNBT(NBTTagCompound p_145839_1_) +@@ -212,12 +253,18 @@ + + if (flag) + { +- this.func_145896_c(8); ++ this.func_145896_c(this.worldObj.getSpigotConfig().hopperTransfer); // Spigot // Cauldron + this.markDirty(); + return true; + } + } + ++ // Spigot start ++ if (!this.func_145888_j()) ++ { ++ this.func_145896_c(this.worldObj.getSpigotConfig().hopperCheck); // Cauldron ++ } ++ // Spigot end + return false; + } + else +@@ -285,18 +332,70 @@ + if (this.getStackInSlot(j) != null) + { + ItemStack itemstack = this.getStackInSlot(j).copy(); +- ItemStack itemstack1 = func_145889_a(iinventory, this.decrStackSize(j, 1), i); +- ++ // CraftBukkit start - Call event when pushing items into other inventories ++ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.decrStackSize(j, 1)); ++ Inventory destinationInventory; ++ ++ // Have to special case large chests as they work oddly ++ if (iinventory instanceof InventoryLargeChest) ++ { ++ destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); ++ } ++ else ++ { ++ // Cauldron start - support mod inventories, with no owners ++ try { ++ if (iinventory.getOwner() != null) { ++ destinationInventory = iinventory.getOwner().getInventory(); ++ } else { ++ // TODO: create a mod inventory for passing to the event, instead of null ++ destinationInventory = null; ++ } ++ } catch (AbstractMethodError e) { // fixes openblocks AbstractMethodError ++ if (iinventory instanceof net.minecraft.tileentity.TileEntity) { ++ org.bukkit.inventory.InventoryHolder holder = net.minecraftforge.cauldron.CauldronUtils.getOwner((net.minecraft.tileentity.TileEntity)iinventory); ++ if (holder != null) { ++ destinationInventory = holder.getInventory(); ++ } else { ++ destinationInventory = null; ++ } ++ } else { ++ destinationInventory = null; ++ } ++ } ++ // Cauldron end ++ } ++ ++ InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); ++ this.getWorldObj().getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ this.setInventorySlotContents(j, itemstack); ++ this.func_145896_c(worldObj.spigotConfig.hopperTransfer); // Spigot ++ return false; ++ } ++ ++ ItemStack itemstack1 = func_145889_a(iinventory, CraftItemStack.asNMSCopy(event.getItem()), i); ++ + if (itemstack1 == null || itemstack1.stackSize == 0) + { +- iinventory.markDirty(); ++ if (event.getItem().equals(oitemstack)) ++ { ++ iinventory.markDirty(); ++ } ++ else ++ { ++ this.setInventorySlotContents(j, itemstack); ++ } ++ ++ // CraftBukkit end + return true; + } +- ++ + this.setInventorySlotContents(j, itemstack); + } + } +- + return false; + } + } +@@ -427,11 +526,70 @@ + if (itemstack != null && func_145890_b(p_145892_1_, itemstack, p_145892_2_, p_145892_3_)) + { + ItemStack itemstack1 = itemstack.copy(); +- ItemStack itemstack2 = func_145889_a(p_145892_0_, p_145892_1_.decrStackSize(p_145892_2_, 1), -1); ++ // CraftBukkit start - Call event on collection of items from inventories into the hopper ++ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(p_145892_1_.decrStackSize(p_145892_2_, 1)); ++ Inventory sourceInventory; + ++ // Have to special case large chests as they work oddly ++ if (p_145892_1_ instanceof InventoryLargeChest) ++ { ++ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) p_145892_1_); ++ } ++ else ++ { ++ // Cauldron start - support mod inventories, with no owners ++ try ++ { ++ if (p_145892_1_.getOwner() != null) ++ { ++ sourceInventory = p_145892_1_.getOwner().getInventory(); ++ } ++ else ++ { ++ // TODO: create a mod inventory for passing to the event, instead of null ++ sourceInventory = null; ++ } ++ } ++ catch (AbstractMethodError e) ++ { ++ sourceInventory = null; ++ } ++ // Cauldron end ++ } ++ ++ InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), p_145892_0_.getOwner().getInventory(), false); ++ p_145892_0_.getWorldObj().getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ p_145892_1_.setInventorySlotContents(p_145892_2_, itemstack1); ++ ++ if (p_145892_0_ instanceof TileEntityHopper) ++ { ++ ((TileEntityHopper) p_145892_0_).func_145896_c(p_145892_0_.getWorldObj().spigotConfig.hopperTransfer); // Spigot ++ } ++ else if (p_145892_0_ instanceof EntityMinecartHopper) ++ { ++ ((EntityMinecartHopper) p_145892_0_).setDisplayTileData(p_145892_0_.getWorldObj().spigotConfig.hopperTransfer / 2); // Spigot ++ } ++ ++ return false; ++ } ++ ++ ItemStack itemstack2 = func_145889_a(p_145892_0_, CraftItemStack.asNMSCopy(event.getItem()), -1); ++ + if (itemstack2 == null || itemstack2.stackSize == 0) + { +- p_145892_1_.markDirty(); ++ if (event.getItem().equals(oitemstack)) ++ { ++ p_145892_1_.markDirty(); ++ } ++ else ++ { ++ p_145892_1_.setInventorySlotContents(p_145892_2_, itemstack1); ++ } ++ ++ // CraftBukkit end + return true; + } + +@@ -451,6 +609,20 @@ + } + else + { ++ // CraftBukkit start ++ // Cauldron start - vanilla compatibility ++ if (p_145898_0_.getOwner() != null && p_145898_1_.getBukkitEntity() != null) ++ { ++ InventoryPickupItemEvent event = new InventoryPickupItemEvent(p_145898_0_.getOwner().getInventory(), (org.bukkit.entity.Item) p_145898_1_.getBukkitEntity()); ++ p_145898_1_.worldObj.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return false; ++ } ++ } ++ // Cauldron end ++ // CraftBukkit end + ItemStack itemstack = p_145898_1_.getEntityItem().copy(); + ItemStack itemstack1 = func_145889_a(p_145898_0_, itemstack, -1); + diff --git a/patches/net/minecraft/tileentity/TileEntityNote.java.patch b/patches/net/minecraft/tileentity/TileEntityNote.java.patch new file mode 100644 index 0000000..834cbaa --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityNote.java.patch @@ -0,0 +1,27 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityNote.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityNote.java +@@ -68,7 +68,23 @@ + b0 = 4; + } + +- p_145878_1_.addBlockEvent(p_145878_2_, p_145878_3_, p_145878_4_, Blocks.noteblock, b0, this.note); ++ // CraftBukkit start ++ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(this.worldObj, p_145878_2_, p_145878_3_, p_145878_4_, b0, this.note); ++ ++ if (!event.isCancelled()) ++ { ++ this.worldObj.addBlockEvent(p_145878_2_, p_145878_3_, p_145878_4_, Blocks.noteblock, event.getInstrument().getType(), event.getNote().getId()); ++ } ++ ++ // CraftBukkit end + } + } ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/tileentity/TileEntityPiston.java.patch b/patches/net/minecraft/tileentity/TileEntityPiston.java.patch new file mode 100644 index 0000000..450ce59 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntityPiston.java.patch @@ -0,0 +1,14 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityPiston.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityPiston.java +@@ -140,6 +140,11 @@ + + public void updateEntity() + { ++ if (this.worldObj == null) ++ { ++ return; // CraftBukkit ++ } ++ + this.lastProgress = this.progress; + + if (this.lastProgress >= 1.0F) diff --git a/patches/net/minecraft/tileentity/TileEntitySign.java.patch b/patches/net/minecraft/tileentity/TileEntitySign.java.patch new file mode 100644 index 0000000..0396749 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntitySign.java.patch @@ -0,0 +1,59 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntitySign.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntitySign.java +@@ -11,7 +11,7 @@ + { + public String[] signText = new String[] {"", "", "", ""}; + public int lineBeingEdited = -1; +- private boolean field_145916_j = true; ++ public boolean field_145916_j = true; // CraftBukkit - private -> public + private EntityPlayer field_145917_k; + private static final String __OBFID = "CL_00000363"; + +@@ -43,7 +43,19 @@ + public Packet getDescriptionPacket() + { + String[] astring = new String[4]; +- System.arraycopy(this.signText, 0, astring, 0, 4); ++ ++ // CraftBukkit start - Limit sign text to 15 chars per line ++ for (int i = 0; i < 4; ++i) ++ { ++ astring[i] = this.signText[i]; ++ ++ if (this.signText[i].length() > 15) ++ { ++ astring[i] = this.signText[i].substring(0, 15); ++ } ++ } ++ ++ // CraftBukkit end + return new S33PacketUpdateSign(this.xCoord, this.yCoord, this.zCoord, astring); + } + +@@ -72,4 +84,26 @@ + { + return this.field_145917_k; + } ++ ++ // CraftBukkit start - central method to limit sign text to 15 chars per line ++ public static String[] sanitizeLines(String[] lines) { ++ String[] astring = new String[4]; ++ for (int i = 0; i < 4; ++i) { ++ astring[i] = lines[i]; ++ ++ if (lines[i].length() > 15) { ++ astring[i] = lines[i].substring(0, 15); ++ } ++ } ++ return astring; ++ } ++ // CraftBukkit end ++ ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/tileentity/TileEntitySkull.java.patch b/patches/net/minecraft/tileentity/TileEntitySkull.java.patch new file mode 100644 index 0000000..8e19707 --- /dev/null +++ b/patches/net/minecraft/tileentity/TileEntitySkull.java.patch @@ -0,0 +1,31 @@ +--- ../src-base/minecraft/net/minecraft/tileentity/TileEntitySkull.java ++++ ../src-work/minecraft/net/minecraft/tileentity/TileEntitySkull.java +@@ -79,6 +79,14 @@ + this.func_152109_d(); + } + ++ // Cauldron start ++ @Override ++ public boolean canUpdate() ++ { ++ return false; ++ } ++ // Cauldron end ++ + private void func_152109_d() + { + if (this.field_152110_j != null && !StringUtils.isNullOrEmpty(this.field_152110_j.getName())) +@@ -113,6 +121,13 @@ + this.field_145910_i = p_145903_1_; + } + ++ // CraftBukkit start ++ public int getRotation() ++ { ++ return this.field_145910_i; ++ } ++ // CraftBukkit end ++ + @SideOnly(Side.CLIENT) + public int func_145906_b() + { diff --git a/patches/net/minecraft/util/EntityDamageSourceIndirect.java.patch b/patches/net/minecraft/util/EntityDamageSourceIndirect.java.patch new file mode 100644 index 0000000..95cb10a --- /dev/null +++ b/patches/net/minecraft/util/EntityDamageSourceIndirect.java.patch @@ -0,0 +1,14 @@ +--- ../src-base/minecraft/net/minecraft/util/EntityDamageSourceIndirect.java ++++ ../src-work/minecraft/net/minecraft/util/EntityDamageSourceIndirect.java +@@ -33,4 +33,11 @@ + String s1 = s + ".item"; + return itemstack != null && itemstack.hasDisplayName() && StatCollector.canTranslate(s1) ? new ChatComponentTranslation(s1, new Object[] {p_151519_1_.func_145748_c_(), ichatcomponent, itemstack.func_151000_E()}): new ChatComponentTranslation(s, new Object[] {p_151519_1_.func_145748_c_(), ichatcomponent}); + } ++ ++ // CraftBukkit start ++ public Entity getProximateDamageSource() ++ { ++ return super.getEntity(); ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/util/FoodStats.java.patch b/patches/net/minecraft/util/FoodStats.java.patch new file mode 100644 index 0000000..262621b --- /dev/null +++ b/patches/net/minecraft/util/FoodStats.java.patch @@ -0,0 +1,85 @@ +--- ../src-base/minecraft/net/minecraft/util/FoodStats.java ++++ ../src-work/minecraft/net/minecraft/util/FoodStats.java +@@ -7,16 +7,31 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.world.EnumDifficulty; ++// CraftBukkit start ++import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.network.play.server.S06PacketUpdateHealth; ++// CraftBukkit end + + public class FoodStats + { +- private int foodLevel = 20; +- private float foodSaturationLevel = 5.0F; +- private float foodExhaustionLevel; +- private int foodTimer; ++ // CraftBukkit start - All made public ++ public int foodLevel = 20; ++ public float foodSaturationLevel = 5.0F; ++ public float foodExhaustionLevel; ++ public int foodTimer; ++ private EntityPlayer entityplayer; ++ // CraftBukkit end + private int prevFoodLevel = 20; + private static final String __OBFID = "CL_00001729"; + ++ // CraftBukkit start - added EntityPlayer constructor ++ public FoodStats(EntityPlayer entityplayer) ++ { ++ org.apache.commons.lang.Validate.notNull(entityplayer); ++ this.entityplayer = entityplayer; ++ } ++ // CraftBukkit end ++ + public void addStats(int p_75122_1_, float p_75122_2_) + { + this.foodLevel = Math.min(p_75122_1_ + this.foodLevel, 20); +@@ -25,7 +40,17 @@ + + public void func_151686_a(ItemFood p_151686_1_, ItemStack p_151686_2_) + { +- this.addStats(p_151686_1_.func_150905_g(p_151686_2_), p_151686_1_.func_150906_h(p_151686_2_)); ++ // CraftBukkit start ++ int oldFoodLevel = foodLevel; ++ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityplayer, p_151686_1_.func_150905_g(p_151686_2_) + oldFoodLevel); ++ ++ if (!event.isCancelled()) ++ { ++ this.addStats(event.getFoodLevel() - oldFoodLevel, p_151686_1_.func_150906_h(p_151686_2_)); ++ } ++ ++ ((EntityPlayerMP) entityplayer).playerNetServerHandler.sendPacket(new S06PacketUpdateHealth(((EntityPlayerMP) entityplayer).getBukkitEntity().getScaledHealth(), entityplayer.getFoodStats().foodLevel, entityplayer.getFoodStats().foodSaturationLevel)); ++ // CraftBukkit end + } + + public void onUpdate(EntityPlayer p_75118_1_) +@@ -43,7 +68,16 @@ + } + else if (enumdifficulty != EnumDifficulty.PEACEFUL) + { +- this.foodLevel = Math.max(this.foodLevel - 1, 0); ++ // CraftBukkit start ++ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(p_75118_1_, Math.max(this.foodLevel - 1, 0)); ++ ++ if (!event.isCancelled()) ++ { ++ this.foodLevel = event.getFoodLevel(); ++ } ++ ++ ((EntityPlayerMP) p_75118_1_).playerNetServerHandler.sendPacket(new S06PacketUpdateHealth(((EntityPlayerMP) p_75118_1_).getBukkitEntity().getScaledHealth(), this.foodLevel, this.foodSaturationLevel)); ++ // CraftBukkit end + } + } + +@@ -53,7 +87,8 @@ + + if (this.foodTimer >= 80) + { +- p_75118_1_.heal(1.0F); ++ // CraftBukkit - added RegainReason ++ p_75118_1_.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED); + this.addExhaustion(3.0F); + this.foodTimer = 0; + } diff --git a/patches/net/minecraft/util/IntHashMap.java.patch b/patches/net/minecraft/util/IntHashMap.java.patch new file mode 100644 index 0000000..a581f46 --- /dev/null +++ b/patches/net/minecraft/util/IntHashMap.java.patch @@ -0,0 +1,29 @@ +--- ../src-base/minecraft/net/minecraft/util/IntHashMap.java ++++ ../src-work/minecraft/net/minecraft/util/IntHashMap.java +@@ -10,7 +10,7 @@ + private int threshold = 12; + private final float growFactor = 0.75F; + private transient volatile int versionStamp; +- private Set keySet = new HashSet(); ++ // private Set keySet = new HashSet(); // CraftBukkit - expensive and unused + private static final String __OBFID = "CL_00001490"; + + private static int computeHash(int p_76044_0_) +@@ -61,7 +61,7 @@ + + public void addKey(int p_76038_1_, Object p_76038_2_) + { +- this.keySet.add(Integer.valueOf(p_76038_1_)); ++ //this.keySet.add(Integer.valueOf(p_76038_1_)); // CraftBukkit + int j = computeHash(p_76038_1_); + int k = getSlotIndex(j, this.slots.length); + +@@ -125,7 +125,7 @@ + + public Object removeObject(int p_76049_1_) + { +- this.keySet.remove(Integer.valueOf(p_76049_1_)); ++ //this.keySet.remove(Integer.valueOf(p_76049_1_)); // CraftBukkit + IntHashMap.Entry entry = this.removeEntry(p_76049_1_); + return entry == null ? null : entry.valueEntry; + } diff --git a/patches/net/minecraft/util/RegistryNamespaced.java.patch b/patches/net/minecraft/util/RegistryNamespaced.java.patch new file mode 100644 index 0000000..5b4f8f3 --- /dev/null +++ b/patches/net/minecraft/util/RegistryNamespaced.java.patch @@ -0,0 +1,36 @@ +--- ../src-base/minecraft/net/minecraft/util/RegistryNamespaced.java ++++ ../src-work/minecraft/net/minecraft/util/RegistryNamespaced.java +@@ -5,6 +5,11 @@ + import java.util.Iterator; + import java.util.Map; + ++// Cauldron start ++import cpw.mods.fml.common.FMLLog; ++import net.minecraft.block.Block; ++// Cauldron end ++ + public class RegistryNamespaced extends RegistrySimple implements IObjectIntIterable + { + protected ObjectIntIdentityMap underlyingIntegerMap = new ObjectIntIdentityMap(); +@@ -18,6 +23,21 @@ + + public void addObject(int p_148756_1_, String p_148756_2_, Object p_148756_3_) + { ++ // Cauldron start - register item/block materials for Bukkit ++ boolean isForgeBlock = p_148756_3_ instanceof Block && (p_148756_3_.getClass().getName().length() > 3 && !p_148756_3_.getClass().getName().startsWith("net.minecraft.block")) ? true : false; ++ org.bukkit.Material material = org.bukkit.Material.addMaterial(p_148756_1_, p_148756_2_, isForgeBlock); ++ if (material != null) ++ { ++ if (isForgeBlock) ++ { ++ FMLLog.info("Injected new Forge block material %s with ID %d.", material.name(), material.getId()); ++ } ++ else ++ { ++ FMLLog.info("Injected new Forge item material %s with ID %d.", material.name(), material.getId()); ++ } ++ } ++ // Cauldron end + this.underlyingIntegerMap.func_148746_a(p_148756_3_, p_148756_1_); + this.putObject(ensureNamespaced(p_148756_2_), p_148756_3_); + } diff --git a/patches/net/minecraft/util/WeightedRandomFishable.java.patch b/patches/net/minecraft/util/WeightedRandomFishable.java.patch new file mode 100644 index 0000000..f5fa100 --- /dev/null +++ b/patches/net/minecraft/util/WeightedRandomFishable.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/net/minecraft/util/WeightedRandomFishable.java ++++ ../src-work/minecraft/net/minecraft/util/WeightedRandomFishable.java +@@ -6,9 +6,11 @@ + + public class WeightedRandomFishable extends WeightedRandom.Item + { +- private final ItemStack field_150711_b; +- private float field_150712_c; +- private boolean field_150710_d; ++ // Cauldron start - private -> public ++ public final ItemStack field_150711_b; ++ public float field_150712_c; ++ public boolean field_150710_d; ++ // Cauldron end + private static final String __OBFID = "CL_00001664"; + + public WeightedRandomFishable(ItemStack p_i45317_1_, int p_i45317_2_) diff --git a/patches/net/minecraft/village/Village.java.patch b/patches/net/minecraft/village/Village.java.patch new file mode 100644 index 0000000..095ddd2 --- /dev/null +++ b/patches/net/minecraft/village/Village.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/village/Village.java ++++ ../src-work/minecraft/net/minecraft/village/Village.java +@@ -71,7 +71,7 @@ + { + EntityIronGolem entityirongolem = new EntityIronGolem(this.worldObj); + entityirongolem.setPosition(vec3.xCoord, vec3.yCoord, vec3.zCoord); +- this.worldObj.spawnEntityInWorld(entityirongolem); ++ this.worldObj.addEntity(entityirongolem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE); // CraftBukkit + ++this.numIronGolems; + } + } diff --git a/patches/net/minecraft/village/VillageSiege.java.patch b/patches/net/minecraft/village/VillageSiege.java.patch new file mode 100644 index 0000000..1abb5c8 --- /dev/null +++ b/patches/net/minecraft/village/VillageSiege.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/village/VillageSiege.java ++++ ../src-work/minecraft/net/minecraft/village/VillageSiege.java +@@ -196,7 +196,7 @@ + } + + entityzombie.setLocationAndAngles(vec3.xCoord, vec3.yCoord, vec3.zCoord, this.worldObj.rand.nextFloat() * 360.0F, 0.0F); +- this.worldObj.spawnEntityInWorld(entityzombie); ++ this.worldObj.addEntity(entityzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_INVASION); // CraftBukkit + ChunkCoordinates chunkcoordinates = this.theVillage.getCenter(); + entityzombie.setHomeArea(chunkcoordinates.posX, chunkcoordinates.posY, chunkcoordinates.posZ, this.theVillage.getVillageRadius()); + return true; diff --git a/patches/net/minecraft/world/Explosion.java.patch b/patches/net/minecraft/world/Explosion.java.patch new file mode 100644 index 0000000..6eee678 --- /dev/null +++ b/patches/net/minecraft/world/Explosion.java.patch @@ -0,0 +1,117 @@ +--- ../src-base/minecraft/net/minecraft/world/Explosion.java ++++ ../src-work/minecraft/net/minecraft/world/Explosion.java +@@ -20,6 +20,12 @@ + import net.minecraft.util.MathHelper; + import net.minecraft.util.Vec3; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityExplodeEvent; ++import org.bukkit.Location; ++// CraftBukkit end ++ + public class Explosion + { + public boolean isFlaming; +@@ -34,6 +40,7 @@ + public float explosionSize; + public List affectedBlockPositions = new ArrayList(); + private Map field_77288_k = new HashMap(); ++ public boolean wasCanceled = false; // CraftBukkit + private static final String __OBFID = "CL_00000134"; + + public Explosion(World p_i1948_1_, Entity p_i1948_2_, double p_i1948_3_, double p_i1948_5_, double p_i1948_7_, float p_i1948_9_) +@@ -48,6 +55,12 @@ + + public void doExplosionA() + { ++ // CraftBukkit start ++ if (this.explosionSize < 0.1F) ++ { ++ return; ++ } ++ // CraftBukkit end + float f = this.explosionSize; + HashSet hashset = new HashSet(); + int i; +@@ -135,7 +148,15 @@ + d7 /= d9; + double d10 = (double)this.worldObj.getBlockDensity(vec3, entity.boundingBox); + double d11 = (1.0D - d4) * d10; +- entity.attackEntityFrom(DamageSource.setExplosionSource(this), (float)((int)((d11 * d11 + d11) / 2.0D * 8.0D * (double)this.explosionSize + 1.0D))); ++ // CraftBukkit start ++ CraftEventFactory.entityDamage = exploder; ++ if (!entity.attackEntityFrom(DamageSource.setExplosionSource(this), (float) ((int) ((d11 * d11 + d11) / 2.0D * 8.0D ++ * (double) this.explosionSize + 1.0D)))) ++ { ++ CraftEventFactory.entityDamage = null; ++ continue; ++ } ++ // CraftBukkit end + double d8 = EnchantmentProtection.func_92092_a(entity, d11); + entity.motionX += d5 * d8; + entity.motionY += d6 * d8; +@@ -174,6 +195,39 @@ + + if (this.isSmoking) + { ++ // CraftBukkit start ++ org.bukkit.World bworld = this.worldObj.getWorld(); ++ org.bukkit.entity.Entity explode = this.exploder == null ? null : this.exploder.getBukkitEntity(); ++ Location location = new Location(bworld, this.explosionX, this.explosionY, this.explosionZ); ++ List blockList = new ArrayList(); ++ ++ for (int i1 = this.affectedBlockPositions.size() - 1; i1 >= 0; i1--) ++ { ++ ChunkPosition cpos = (ChunkPosition) this.affectedBlockPositions.get(i1); ++ org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.chunkPosX, cpos.chunkPosY, cpos.chunkPosZ); ++ ++ if (bblock.getType() != org.bukkit.Material.AIR) ++ { ++ blockList.add(bblock); ++ } ++ } ++ ++ EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 0.3F); ++ this.worldObj.getServer().getPluginManager().callEvent(event); ++ this.affectedBlockPositions.clear(); ++ ++ for (org.bukkit.block.Block bblock : event.blockList()) ++ { ++ ChunkPosition coords = new ChunkPosition(bblock.getX(), bblock.getY(), bblock.getZ()); ++ affectedBlockPositions.add(coords); ++ } ++ ++ if (event.isCancelled()) ++ { ++ this.wasCanceled = true; ++ return; ++ } ++ // CraftBukkit end + iterator = this.affectedBlockPositions.iterator(); + + while (iterator.hasNext()) +@@ -209,7 +263,8 @@ + { + if (block.canDropFromExplosion(this)) + { +- block.dropBlockAsItemWithChance(this.worldObj, i, j, k, this.worldObj.getBlockMetadata(i, j, k), 1.0F / this.explosionSize, 0); ++ // CraftBukkit - add yield ++ block.dropBlockAsItemWithChance(this.worldObj, i, j, k, this.worldObj.getBlockMetadata(i, j, k), event.getYield(), 0); + } + + block.onBlockExploded(this.worldObj, i, j, k, this); +@@ -232,7 +287,12 @@ + + if (block.getMaterial() == Material.air && block1.func_149730_j() && this.explosionRNG.nextInt(3) == 0) + { +- this.worldObj.setBlock(i, j, k, Blocks.fire); ++ // CraftBukkit start - Ignition by explosion ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.worldObj, i, j, k, this).isCancelled()) ++ { ++ this.worldObj.setBlock(i, j, k, Blocks.fire); ++ } ++ // CraftBukkit end + } + } + } diff --git a/patches/net/minecraft/world/SpawnerAnimals.java.patch b/patches/net/minecraft/world/SpawnerAnimals.java.patch new file mode 100644 index 0000000..5b040f0 --- /dev/null +++ b/patches/net/minecraft/world/SpawnerAnimals.java.patch @@ -0,0 +1,130 @@ +--- ../src-base/minecraft/net/minecraft/world/SpawnerAnimals.java ++++ ../src-work/minecraft/net/minecraft/world/SpawnerAnimals.java +@@ -22,9 +22,15 @@ + import cpw.mods.fml.common.eventhandler.Event.Result; + import net.minecraftforge.event.ForgeEventFactory; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.util.LongHash; ++import org.bukkit.craftbukkit.util.LongObjectHashMap; ++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; ++// CraftBukkit end ++ + public final class SpawnerAnimals + { +- private HashMap eligibleChunksForSpawning = new HashMap(); ++ private LongObjectHashMap eligibleChunksForSpawning = new LongObjectHashMap(); // CraftBukkit - HashMap -> LongObjectHashMap + private static final String __OBFID = "CL_00000152"; + + protected static ChunkPosition func_151350_a(World p_151350_0_, int p_151350_1_, int p_151350_2_) +@@ -54,22 +60,30 @@ + int j = MathHelper.floor_double(entityplayer.posX / 16.0D); + k = MathHelper.floor_double(entityplayer.posZ / 16.0D); + byte b0 = 8; ++ // Spigot Start ++ b0 = p_77192_1_.getSpigotConfig().mobSpawnRange; // Cauldron ++ b0 = (b0 > p_77192_1_.getSpigotConfig().viewDistance) ? (byte) p_77192_1_.spigotConfig.viewDistance : b0; // Cauldron ++ b0 = (b0 > 8) ? 8 : b0; ++ // Spigot End + + for (int l = -b0; l <= b0; ++l) + { + for (int i1 = -b0; i1 <= b0; ++i1) + { + boolean flag3 = l == -b0 || l == b0 || i1 == -b0 || i1 == b0; +- ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(l + j, i1 + k); ++ // CraftBukkit start ++ long chunkCoords = LongHash.toLong(l + j, i1 + k); + + if (!flag3) + { +- this.eligibleChunksForSpawning.put(chunkcoordintpair, Boolean.valueOf(false)); ++ this.eligibleChunksForSpawning.put(chunkCoords, false); + } +- else if (!this.eligibleChunksForSpawning.containsKey(chunkcoordintpair)) ++ else if (!this.eligibleChunksForSpawning.containsKey(chunkCoords)) + { +- this.eligibleChunksForSpawning.put(chunkcoordintpair, Boolean.valueOf(true)); ++ this.eligibleChunksForSpawning.put(chunkCoords, true); + } ++ ++ // CraftBukkit end + } + } + } +@@ -82,22 +96,46 @@ + for (int k3 = 0; k3 < k; ++k3) + { + EnumCreatureType enumcreaturetype = aenumcreaturetype[k3]; ++ // CraftBukkit start - Use per-world spawn limits ++ int limit = enumcreaturetype.getMaxNumberOfCreature(); + +- if ((!enumcreaturetype.getPeacefulCreature() || p_77192_3_) && (enumcreaturetype.getPeacefulCreature() || p_77192_2_) && (!enumcreaturetype.getAnimal() || p_77192_4_) && p_77192_1_.countEntities(enumcreaturetype, true) <= enumcreaturetype.getMaxNumberOfCreature() * this.eligibleChunksForSpawning.size() / 256) ++ switch (enumcreaturetype) + { ++ case monster: ++ limit = p_77192_1_.getWorld().getMonsterSpawnLimit(); ++ break; ++ case creature: ++ limit = p_77192_1_.getWorld().getAnimalSpawnLimit(); ++ break; ++ case waterCreature: ++ limit = p_77192_1_.getWorld().getWaterAnimalSpawnLimit(); ++ break; ++ case ambient: ++ limit = p_77192_1_.getWorld().getAmbientSpawnLimit(); ++ break; ++ } ++ ++ if (limit == 0) ++ { ++ continue; ++ } ++ ++ // CraftBukkit end ++ ++ if ((!enumcreaturetype.getPeacefulCreature() || p_77192_3_) && (enumcreaturetype.getPeacefulCreature() || p_77192_2_) && (!enumcreaturetype.getAnimal() || p_77192_4_) && p_77192_1_.countEntities(enumcreaturetype.getCreatureClass()) <= limit * eligibleChunksForSpawning.size() / 256) // CraftBukkit - use per-world limits ++ { + Iterator iterator = this.eligibleChunksForSpawning.keySet().iterator(); +- ArrayList tmp = new ArrayList(eligibleChunksForSpawning.keySet()); +- Collections.shuffle(tmp); +- iterator = tmp.iterator(); + label110: + + while (iterator.hasNext()) + { +- ChunkCoordIntPair chunkcoordintpair1 = (ChunkCoordIntPair)iterator.next(); ++ // CraftBukkit start ++ long key = ((Long) iterator.next()).longValue(); + +- if (!((Boolean)this.eligibleChunksForSpawning.get(chunkcoordintpair1)).booleanValue()) ++ if (!(this.eligibleChunksForSpawning.get(key))) + { +- ChunkPosition chunkposition = func_151350_a(p_77192_1_, chunkcoordintpair1.chunkXPos, chunkcoordintpair1.chunkZPos); ++ ChunkPosition chunkposition = func_151350_a(p_77192_1_, LongHash.msw(key), LongHash.lsw(key)); ++ // CraftBukkit end + int j1 = chunkposition.chunkPosX; + int k1 = chunkposition.chunkPosY; + int l1 = chunkposition.chunkPosZ; +@@ -170,7 +208,8 @@ + if (canSpawn == Result.ALLOW || (canSpawn == Result.DEFAULT && entityliving.getCanSpawnHere())) + { + ++i2; +- p_77192_1_.spawnEntityInWorld(entityliving); ++ // CraftBukkit start - Added a reason for spawning this creature, moved entityliving.onSpawnWithEgg(ientitylivingdata) up ++ p_77192_1_.addEntity(entityliving, SpawnReason.NATURAL); + if (!ForgeEventFactory.doSpecialSpawn(entityliving, p_77192_1_, f, f1, f2)) + { + ientitylivingdata = entityliving.onSpawnWithEgg(ientitylivingdata); +@@ -266,8 +305,10 @@ + } + + entityliving.setLocationAndAngles((double)f, (double)f1, (double)f2, p_77191_6_.nextFloat() * 360.0F, 0.0F); +- p_77191_0_.spawnEntityInWorld(entityliving); ++ // CraftBukkit start - Added a reason for spawning this creature, moved entityliving.a(ientitylivingdata) up + ientitylivingdata = entityliving.onSpawnWithEgg(ientitylivingdata); ++ p_77191_0_.addEntity(entityliving, SpawnReason.CHUNK_GEN); ++ // CraftBukkit end + flag = true; + } + diff --git a/patches/net/minecraft/world/Teleporter.java.patch b/patches/net/minecraft/world/Teleporter.java.patch new file mode 100644 index 0000000..e09bfa5 --- /dev/null +++ b/patches/net/minecraft/world/Teleporter.java.patch @@ -0,0 +1,838 @@ +--- ../src-base/minecraft/net/minecraft/world/Teleporter.java ++++ ../src-work/minecraft/net/minecraft/world/Teleporter.java +@@ -12,6 +12,12 @@ + import net.minecraft.util.LongHashMap; + import net.minecraft.util.MathHelper; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.event.entity.EntityPortalExitEvent; ++import org.bukkit.util.Vector; ++// CraftBukkit end ++ + public class Teleporter + { + private final WorldServer worldServerInstance; +@@ -20,100 +26,172 @@ + private final List destinationCoordinateKeys = new ArrayList(); + private static final String __OBFID = "CL_00000153"; + +- public Teleporter(WorldServer p_i1963_1_) ++ public Teleporter(WorldServer par1WorldServer) + { +- this.worldServerInstance = p_i1963_1_; +- this.random = new Random(p_i1963_1_.getSeed()); ++ this.worldServerInstance = par1WorldServer; ++ this.random = new Random(par1WorldServer.getSeed()); + } + +- public void placeInPortal(Entity p_77185_1_, double p_77185_2_, double p_77185_4_, double p_77185_6_, float p_77185_8_) ++ public void placeInPortal(Entity par1Entity, double par2, double par4, double par6, float par8) + { + if (this.worldServerInstance.provider.dimensionId != 1) + { +- if (!this.placeInExistingPortal(p_77185_1_, p_77185_2_, p_77185_4_, p_77185_6_, p_77185_8_)) ++ if (!this.placeInExistingPortal(par1Entity, par2, par4, par6, par8)) + { +- this.makePortal(p_77185_1_); +- this.placeInExistingPortal(p_77185_1_, p_77185_2_, p_77185_4_, p_77185_6_, p_77185_8_); ++ this.makePortal(par1Entity); ++ this.placeInExistingPortal(par1Entity, par2, par4, par6, par8); + } + } + else + { +- int i = MathHelper.floor_double(p_77185_1_.posX); +- int j = MathHelper.floor_double(p_77185_1_.posY) - 1; +- int k = MathHelper.floor_double(p_77185_1_.posZ); +- byte b0 = 1; +- byte b1 = 0; ++ // CraftBukkit start - Modularize end portal creation ++ ChunkCoordinates created = this.createEndPortal(par2, par4, par6); ++ par1Entity.setLocationAndAngles((double) created.posX, (double) created.posY, (double) created.posZ, par1Entity.rotationYaw, 0.0F); ++ par1Entity.motionX = par1Entity.motionY = par1Entity.motionZ = 0.0D; ++ } ++ } + +- for (int l = -2; l <= 2; ++l) ++ // Split out from original a(Entity, double, double, double, float) method in order to enable being called from createPortal ++ private ChunkCoordinates createEndPortal(double x, double y, double z) ++ { ++ int i = MathHelper.floor_double(x); ++ int j = MathHelper.floor_double(y) - 1; ++ int k = MathHelper.floor_double(z); ++ // CraftBukkit end ++ byte b0 = 1; ++ byte b1 = 0; ++ ++ for (int l = -2; l <= 2; ++l) ++ { ++ for (int i1 = -2; i1 <= 2; ++i1) + { +- for (int i1 = -2; i1 <= 2; ++i1) ++ for (int j1 = -1; j1 < 3; ++j1) + { +- for (int j1 = -1; j1 < 3; ++j1) ++ int k1 = i + i1 * b0 + l * b1; ++ int l1 = j + j1; ++ int i2 = k + i1 * b1 - l * b0; ++ boolean flag = j1 < 0; ++ this.worldServerInstance.setBlock(k1, l1, i2, flag ? Blocks.obsidian : Blocks.air); ++ } ++ } ++ } ++ ++ // CraftBukkit start ++ return new ChunkCoordinates(i, j, k); ++ } ++ ++ // use logic based on creation to verify end portal ++ private ChunkCoordinates findEndPortal(ChunkCoordinates portal) ++ { ++ int i = portal.posX; ++ int j = portal.posY - 1; ++ int k = portal.posZ; ++ byte b0 = 1; ++ byte b1 = 0; ++ ++ for (int l = -2; l <= 2; ++l) ++ { ++ for (int i1 = -2; i1 <= 2; ++i1) ++ { ++ for (int j1 = -1; j1 < 3; ++j1) ++ { ++ int k1 = i + i1 * b0 + l * b1; ++ int l1 = j + j1; ++ int i2 = k + i1 * b1 - l * b0; ++ boolean flag = j1 < 0; ++ ++ if (this.worldServerInstance.getBlock(k1, l1, i2) != (flag ? Blocks.obsidian : Blocks.air)) + { +- int k1 = i + i1 * b0 + l * b1; +- int l1 = j + j1; +- int i2 = k + i1 * b1 - l * b0; +- boolean flag = j1 < 0; +- this.worldServerInstance.setBlock(k1, l1, i2, flag ? Blocks.obsidian : Blocks.air); ++ return null; + } + } + } ++ } + +- p_77185_1_.setLocationAndAngles((double)i, (double)j, (double)k, p_77185_1_.rotationYaw, 0.0F); +- p_77185_1_.motionX = p_77185_1_.motionY = p_77185_1_.motionZ = 0.0D; ++ return new ChunkCoordinates(i, j, k); ++ } ++ // CraftBukkit end ++ ++ public boolean placeInExistingPortal(Entity par1Entity, double par2, double par4, double par6, float par8) ++ { ++ // CraftBukkit start - Modularize portal search process and entity teleportation ++ ChunkCoordinates found = this.findPortal(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 128); ++ ++ if (found == null) ++ { ++ return false; + } ++ ++ Location exit = new Location(this.worldServerInstance.getWorld(), found.posX, found.posY, found.posZ, par8, par1Entity.rotationPitch); ++ Vector velocity = par1Entity.getBukkitEntity().getVelocity(); ++ this.adjustExit(par1Entity, exit, velocity); ++ par1Entity.setLocationAndAngles(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); ++ ++ if (par1Entity.motionX != velocity.getX() || par1Entity.motionY != velocity.getY() || par1Entity.motionZ != velocity.getZ()) ++ { ++ par1Entity.getBukkitEntity().setVelocity(velocity); ++ } ++ ++ return true; + } + +- public boolean placeInExistingPortal(Entity p_77184_1_, double p_77184_2_, double p_77184_4_, double p_77184_6_, float p_77184_8_) ++ public ChunkCoordinates findPortal(double x, double y, double z, int short1) + { +- short short1 = 128; ++ if (this.worldServerInstance.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) ++ { ++ return this.findEndPortal(this.worldServerInstance.provider.getEntrancePortalLocation()); ++ } ++ ++ // CraftBukkit end + double d3 = -1.0D; + int i = 0; + int j = 0; + int k = 0; +- int l = MathHelper.floor_double(p_77184_1_.posX); +- int i1 = MathHelper.floor_double(p_77184_1_.posZ); ++ // CraftBukkit start ++ int l = MathHelper.floor_double(x); ++ int i1 = MathHelper.floor_double(z); ++ // CraftBukkit end + long j1 = ChunkCoordIntPair.chunkXZ2Int(l, i1); + boolean flag = true; +- double d7; +- int l3; ++ double d4; ++ int k1; + + if (this.destinationCoordinateCache.containsItem(j1)) + { +- Teleporter.PortalPosition portalposition = (Teleporter.PortalPosition)this.destinationCoordinateCache.getValueByKey(j1); ++ PortalPosition chunkcoordinatesportal = (PortalPosition) this.destinationCoordinateCache.getValueByKey(j1); + d3 = 0.0D; +- i = portalposition.posX; +- j = portalposition.posY; +- k = portalposition.posZ; +- portalposition.lastUpdateTime = this.worldServerInstance.getTotalWorldTime(); ++ i = chunkcoordinatesportal.posX; ++ j = chunkcoordinatesportal.posY; ++ k = chunkcoordinatesportal.posZ; ++ chunkcoordinatesportal.lastUpdateTime = this.worldServerInstance.getTotalWorldTime(); + flag = false; + } + else + { +- for (l3 = l - short1; l3 <= l + short1; ++l3) ++ for (k1 = l - short1; k1 <= l + short1; ++k1) + { +- double d4 = (double)l3 + 0.5D - p_77184_1_.posX; ++ double d5 = (double) k1 + 0.5D - x; // CraftBukkit + + for (int l1 = i1 - short1; l1 <= i1 + short1; ++l1) + { +- double d5 = (double)l1 + 0.5D - p_77184_1_.posZ; ++ double d6 = (double) l1 + 0.5D - z; // CraftBukkit + + for (int i2 = this.worldServerInstance.getActualHeight() - 1; i2 >= 0; --i2) + { +- if (this.worldServerInstance.getBlock(l3, i2, l1) == Blocks.portal) ++ if (this.worldServerInstance.getBlock(k1, i2, l1) == Blocks.portal) + { +- while (this.worldServerInstance.getBlock(l3, i2 - 1, l1) == Blocks.portal) ++ while (this.worldServerInstance.getBlock(k1, i2 - 1, l1) == Blocks.portal) + { + --i2; + } + +- d7 = (double)i2 + 0.5D - p_77184_1_.posY; +- double d8 = d4 * d4 + d7 * d7 + d5 * d5; ++ d4 = (double) i2 + 0.5D - y; // CraftBukkit ++ double d7 = d5 * d5 + d4 * d4 + d6 * d6; + +- if (d3 < 0.0D || d8 < d3) ++ if (d3 < 0.0D || d7 < d3) + { +- d3 = d8; +- i = l3; ++ d3 = d7; ++ i = k1; + j = i2; + k = l1; + } +@@ -127,61 +205,93 @@ + { + if (flag) + { +- this.destinationCoordinateCache.add(j1, new Teleporter.PortalPosition(i, j, k, this.worldServerInstance.getTotalWorldTime())); ++ this.destinationCoordinateCache.add(j1, new PortalPosition(i, j, k, this.worldServerInstance.getTotalWorldTime())); + this.destinationCoordinateKeys.add(Long.valueOf(j1)); + } + +- double d11 = (double)i + 0.5D; +- double d6 = (double)j + 0.5D; +- d7 = (double)k + 0.5D; +- int i4 = -1; ++ // CraftBukkit start - Moved entity teleportation logic into exit ++ return new ChunkCoordinates(i, j, k); ++ } ++ else ++ { ++ return null; ++ } ++ } ++ // Entity repositioning logic split out from original b method and combined with repositioning logic for The End from original a method ++ public void adjustExit(Entity entity, Location position, Vector velocity) ++ { ++ Location from = position.clone(); ++ Vector before = velocity.clone(); ++ int i = position.getBlockX(); ++ int j = position.getBlockY(); ++ int k = position.getBlockZ(); ++ float f = position.getYaw(); + ++ if (this.worldServerInstance.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) ++ { ++ // entity.setPositionRotation((double) i, (double) j, (double) k, entity.yaw, 0.0F); ++ // entity.motX = entity.motY = entity.motZ = 0.0D; ++ position.setPitch(0.0F); ++ velocity.setX(0); ++ velocity.setY(0); ++ velocity.setZ(0); ++ } ++ else ++ { ++ double d4; ++ int k1; ++ // CraftBukkit end ++ double d8 = (double) i + 0.5D; ++ double d9 = (double) j + 0.5D; ++ d4 = (double) k + 0.5D; ++ int j2 = -1; ++ + if (this.worldServerInstance.getBlock(i - 1, j, k) == Blocks.portal) + { +- i4 = 2; ++ j2 = 2; + } + + if (this.worldServerInstance.getBlock(i + 1, j, k) == Blocks.portal) + { +- i4 = 0; ++ j2 = 0; + } + + if (this.worldServerInstance.getBlock(i, j, k - 1) == Blocks.portal) + { +- i4 = 3; ++ j2 = 3; + } + + if (this.worldServerInstance.getBlock(i, j, k + 1) == Blocks.portal) + { +- i4 = 1; ++ j2 = 1; + } + +- int j2 = p_77184_1_.getTeleportDirection(); ++ int k2 = entity.getTeleportDirection(); + +- if (i4 > -1) ++ if (j2 > -1) + { +- int k2 = Direction.rotateLeft[i4]; +- int l2 = Direction.offsetX[i4]; +- int i3 = Direction.offsetZ[i4]; +- int j3 = Direction.offsetX[k2]; +- int k3 = Direction.offsetZ[k2]; +- boolean flag1 = !this.worldServerInstance.isAirBlock(i + l2 + j3, j, k + i3 + k3) || !this.worldServerInstance.isAirBlock(i + l2 + j3, j + 1, k + i3 + k3); +- boolean flag2 = !this.worldServerInstance.isAirBlock(i + l2, j, k + i3) || !this.worldServerInstance.isAirBlock(i + l2, j + 1, k + i3); ++ int l2 = Direction.rotateLeft[j2]; ++ int i3 = Direction.offsetX[j2]; ++ int j3 = Direction.offsetZ[j2]; ++ int k3 = Direction.offsetX[l2]; ++ int l3 = Direction.offsetZ[l2]; ++ boolean flag1 = !this.worldServerInstance.isAirBlock(i + i3 + k3, j, k + j3 + l3) || !this.worldServerInstance.isAirBlock(i + i3 + k3, j + 1, k + j3 + l3); ++ boolean flag2 = !this.worldServerInstance.isAirBlock(i + i3, j, k + j3) || !this.worldServerInstance.isAirBlock(i + i3, j + 1, k + j3); + + if (flag1 && flag2) + { +- i4 = Direction.rotateOpposite[i4]; +- k2 = Direction.rotateOpposite[k2]; +- l2 = Direction.offsetX[i4]; +- i3 = Direction.offsetZ[i4]; +- j3 = Direction.offsetX[k2]; +- k3 = Direction.offsetZ[k2]; +- l3 = i - j3; +- d11 -= (double)j3; +- int k1 = k - k3; +- d7 -= (double)k3; +- flag1 = !this.worldServerInstance.isAirBlock(l3 + l2 + j3, j, k1 + i3 + k3) || !this.worldServerInstance.isAirBlock(l3 + l2 + j3, j + 1, k1 + i3 + k3); +- flag2 = !this.worldServerInstance.isAirBlock(l3 + l2, j, k1 + i3) || !this.worldServerInstance.isAirBlock(l3 + l2, j + 1, k1 + i3); ++ j2 = Direction.rotateOpposite[j2]; ++ l2 = Direction.rotateOpposite[l2]; ++ i3 = Direction.offsetX[j2]; ++ j3 = Direction.offsetZ[j2]; ++ k3 = Direction.offsetX[l2]; ++ l3 = Direction.offsetZ[l2]; ++ k1 = i - k3; ++ d8 -= (double) k3; ++ int i4 = k - l3; ++ d4 -= (double) l3; ++ flag1 = !this.worldServerInstance.isAirBlock(k1 + i3 + k3, j, i4 + j3 + l3) || !this.worldServerInstance.isAirBlock(k1 + i3 + k3, j + 1, i4 + j3 + l3); ++ flag2 = !this.worldServerInstance.isAirBlock(k1 + i3, j, i4 + j3) || !this.worldServerInstance.isAirBlock(k1 + i3, j + 1, i4 + j3); + } + + float f1 = 0.5F; +@@ -200,24 +310,24 @@ + f2 = 0.0F; + } + +- d11 += (double)((float)j3 * f1 + f2 * (float)l2); +- d7 += (double)((float)k3 * f1 + f2 * (float)i3); ++ d8 += (double)((float) k3 * f1 + f2 * (float) i3); ++ d4 += (double)((float) l3 * f1 + f2 * (float) j3); + float f3 = 0.0F; + float f4 = 0.0F; + float f5 = 0.0F; + float f6 = 0.0F; + +- if (i4 == j2) ++ if (j2 == k2) + { + f3 = 1.0F; + f4 = 1.0F; + } +- else if (i4 == Direction.rotateOpposite[j2]) ++ else if (j2 == Direction.rotateOpposite[k2]) + { + f3 = -1.0F; + f4 = -1.0F; + } +- else if (i4 == Direction.rotateRight[j2]) ++ else if (j2 == Direction.rotateRight[k2]) + { + f5 = 1.0F; + f6 = -1.0F; +@@ -228,33 +338,77 @@ + f6 = 1.0F; + } + +- double d9 = p_77184_1_.motionX; +- double d10 = p_77184_1_.motionZ; +- p_77184_1_.motionX = d9 * (double)f3 + d10 * (double)f6; +- p_77184_1_.motionZ = d9 * (double)f5 + d10 * (double)f4; +- p_77184_1_.rotationYaw = p_77184_8_ - (float)(j2 * 90) + (float)(i4 * 90); ++ // CraftBukkit start ++ double d10 = velocity.getX(); ++ double d11 = velocity.getZ(); ++ // CraftBukkit end ++ // CraftBukkit start - Adjust position and velocity instances instead of entity ++ velocity.setX(d10 * (double) f3 + d11 * (double) f6); ++ velocity.setZ(d10 * (double) f5 + d11 * (double) f4); ++ f = f - (float)(k2 * 90) + (float)(j2 * 90); + } + else + { +- p_77184_1_.motionX = p_77184_1_.motionY = p_77184_1_.motionZ = 0.0D; ++ // entity.motX = entity.motY = entity.motZ = 0.0D; ++ velocity.setX(0); ++ velocity.setY(0); ++ velocity.setZ(0); + } + +- p_77184_1_.setLocationAndAngles(d11, d6, d7, p_77184_1_.rotationYaw, p_77184_1_.rotationPitch); +- return true; ++ // entity.setPositionRotation(d8, d9, d4, entity.yaw, entity.pitch); ++ position.setX(d8); ++ position.setY(d9); ++ position.setZ(d4); ++ position.setYaw(f); + } ++ ++ EntityPortalExitEvent event = new EntityPortalExitEvent(entity.getBukkitEntity(), from, position, before, velocity); ++ this.worldServerInstance.getServer().getPluginManager().callEvent(event); ++ Location to = event.getTo(); ++ ++ if (event.isCancelled() || to == null || !entity.isEntityAlive()) ++ { ++ position.setX(from.getX()); ++ position.setY(from.getY()); ++ position.setZ(from.getZ()); ++ position.setYaw(from.getYaw()); ++ position.setPitch(from.getPitch()); ++ velocity.copy(before); ++ } + else + { +- return false; ++ position.setX(to.getX()); ++ position.setY(to.getY()); ++ position.setZ(to.getZ()); ++ position.setYaw(to.getYaw()); ++ position.setPitch(to.getPitch()); ++ velocity.copy(event.getAfter()); // event.getAfter() will never be null, as setAfter() will cause an NPE if null is passed in + } ++ ++ // CraftBukkit end + } + +- public boolean makePortal(Entity p_85188_1_) ++ public boolean makePortal(Entity par1Entity) + { +- byte b0 = 16; ++ // CraftBukkit start - Allow for portal creation to be based on coordinates instead of entity ++ return this.createPortal(par1Entity.posX, par1Entity.posY, par1Entity.posZ, 16); ++ } ++ ++ public boolean createPortal(double x, double y, double z, int b0) ++ { ++ if (this.worldServerInstance.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) ++ { ++ this.createEndPortal(x, y, z); ++ return true; ++ } ++ ++ // CraftBukkit end + double d0 = -1.0D; +- int i = MathHelper.floor_double(p_85188_1_.posX); +- int j = MathHelper.floor_double(p_85188_1_.posY); +- int k = MathHelper.floor_double(p_85188_1_.posZ); ++ // CraftBukkit start ++ int i = MathHelper.floor_double(x); ++ int j = MathHelper.floor_double(y); ++ int k = MathHelper.floor_double(z); ++ // CraftBukkit end + int l = i; + int i1 = j; + int j1 = k; +@@ -262,8 +416,10 @@ + int l1 = this.random.nextInt(4); + int i2; + double d1; +- int k2; + double d2; ++ int j2; ++ int k2; ++ int l2; + int i3; + int j3; + int k3; +@@ -271,125 +427,123 @@ + int i4; + int j4; + int k4; +- int l4; +- int i5; + double d3; + double d4; +- ++ + for (i2 = i - b0; i2 <= i + b0; ++i2) + { +- d1 = (double)i2 + 0.5D - p_85188_1_.posX; +- +- for (k2 = k - b0; k2 <= k + b0; ++k2) ++ d1 = (double) i2 + 0.5D - x; // CraftBukkit ++ ++ for (j2 = k - b0; j2 <= k + b0; ++j2) + { +- d2 = (double)k2 + 0.5D - p_85188_1_.posZ; ++ d2 = (double) j2 + 0.5D - z; // CraftBukkit + label274: +- +- for (i3 = this.worldServerInstance.getActualHeight() - 1; i3 >= 0; --i3) ++ ++ for (k2 = this.worldServerInstance.getActualHeight() - 1; k2 >= 0; --k2) + { +- if (this.worldServerInstance.isAirBlock(i2, i3, k2)) ++ if (this.worldServerInstance.isAirBlock(i2, k2, j2)) + { +- while (i3 > 0 && this.worldServerInstance.isAirBlock(i2, i3 - 1, k2)) ++ while (k2 > 0 && this.worldServerInstance.isAirBlock(i2, k2 - 1, j2)) + { +- --i3; ++ --k2; + } +- +- for (j3 = l1; j3 < l1 + 4; ++j3) ++ ++ for (i3 = l1; i3 < l1 + 4; ++i3) + { +- k3 = j3 % 2; +- l3 = 1 - k3; +- +- if (j3 % 4 >= 2) ++ l2 = i3 % 2; ++ k3 = 1 - l2; ++ ++ if (i3 % 4 >= 2) + { ++ l2 = -l2; + k3 = -k3; +- l3 = -l3; + } +- +- for (i4 = 0; i4 < 3; ++i4) ++ ++ for (j3 = 0; j3 < 3; ++j3) + { +- for (j4 = 0; j4 < 4; ++j4) ++ for (i4 = 0; i4 < 4; ++i4) + { +- for (k4 = -1; k4 < 4; ++k4) ++ for (l3 = -1; l3 < 4; ++l3) + { +- l4 = i2 + (j4 - 1) * k3 + i4 * l3; +- i5 = i3 + k4; +- int j5 = k2 + (j4 - 1) * l3 - i4 * k3; +- +- if (k4 < 0 && !this.worldServerInstance.getBlock(l4, i5, j5).getMaterial().isSolid() || k4 >= 0 && !this.worldServerInstance.isAirBlock(l4, i5, j5)) ++ k4 = i2 + (i4 - 1) * l2 + j3 * k3; ++ j4 = k2 + l3; ++ int l4 = j2 + (i4 - 1) * k3 - j3 * l2; ++ ++ if (l3 < 0 && !this.worldServerInstance.getBlock(k4, j4, l4).getMaterial().isSolid() || l3 >= 0 && !this.worldServerInstance.isAirBlock(k4, j4, l4)) + { + continue label274; + } + } + } + } +- +- d3 = (double)i3 + 0.5D - p_85188_1_.posY; ++ ++ d3 = (double) k2 + 0.5D - y; // CraftBukkit + d4 = d1 * d1 + d3 * d3 + d2 * d2; +- ++ + if (d0 < 0.0D || d4 < d0) + { + d0 = d4; + l = i2; +- i1 = i3; +- j1 = k2; +- k1 = j3 % 4; ++ i1 = k2; ++ j1 = j2; ++ k1 = i3 % 4; + } + } + } + } + } + } +- ++ + if (d0 < 0.0D) + { + for (i2 = i - b0; i2 <= i + b0; ++i2) + { +- d1 = (double)i2 + 0.5D - p_85188_1_.posX; +- +- for (k2 = k - b0; k2 <= k + b0; ++k2) ++ d1 = (double) i2 + 0.5D - x; // CraftBukkit ++ ++ for (j2 = k - b0; j2 <= k + b0; ++j2) + { +- d2 = (double)k2 + 0.5D - p_85188_1_.posZ; ++ d2 = (double) j2 + 0.5D - z; // CraftBukkit + label222: +- +- for (i3 = this.worldServerInstance.getActualHeight() - 1; i3 >= 0; --i3) ++ ++ for (k2 = this.worldServerInstance.getActualHeight() - 1; k2 >= 0; --k2) + { +- if (this.worldServerInstance.isAirBlock(i2, i3, k2)) ++ if (this.worldServerInstance.isAirBlock(i2, k2, j2)) + { +- while (i3 > 0 && this.worldServerInstance.isAirBlock(i2, i3 - 1, k2)) ++ while (k2 > 0 && this.worldServerInstance.isAirBlock(i2, k2 - 1, j2)) + { +- --i3; ++ --k2; + } +- +- for (j3 = l1; j3 < l1 + 2; ++j3) ++ ++ for (i3 = l1; i3 < l1 + 2; ++i3) + { +- k3 = j3 % 2; +- l3 = 1 - k3; +- +- for (i4 = 0; i4 < 4; ++i4) ++ l2 = i3 % 2; ++ k3 = 1 - l2; ++ ++ for (j3 = 0; j3 < 4; ++j3) + { +- for (j4 = -1; j4 < 4; ++j4) ++ for (i4 = -1; i4 < 4; ++i4) + { +- k4 = i2 + (i4 - 1) * k3; +- l4 = i3 + j4; +- i5 = k2 + (i4 - 1) * l3; +- +- if (j4 < 0 && !this.worldServerInstance.getBlock(k4, l4, i5).getMaterial().isSolid() || j4 >= 0 && !this.worldServerInstance.isAirBlock(k4, l4, i5)) ++ l3 = i2 + (j3 - 1) * l2; ++ k4 = k2 + i4; ++ j4 = j2 + (j3 - 1) * k3; ++ ++ if (i4 < 0 && !this.worldServerInstance.getBlock(l3, k4, j4).getMaterial().isSolid() || i4 >= 0 && !this.worldServerInstance.isAirBlock(l3, k4, j4)) + { + continue label222; + } + } + } +- +- d3 = (double)i3 + 0.5D - p_85188_1_.posY; ++ ++ d3 = (double) k2 + 0.5D - y; // CraftBukkit + d4 = d1 * d1 + d3 * d3 + d2 * d2; +- ++ + if (d0 < 0.0D || d4 < d0) + { + d0 = d4; + l = i2; +- i1 = i3; +- j1 = k2; +- k1 = j3 % 2; ++ i1 = k2; ++ j1 = j2; ++ k1 = i3 % 2; + } + } + } +@@ -397,93 +551,93 @@ + } + } + } +- +- int k5 = l; +- int j2 = i1; +- k2 = j1; +- int l5 = k1 % 2; +- int l2 = 1 - l5; +- ++ ++ int i5 = l; ++ int j5 = i1; ++ j2 = j1; ++ int k5 = k1 % 2; ++ int l5 = 1 - k5; ++ + if (k1 % 4 >= 2) + { ++ k5 = -k5; + l5 = -l5; +- l2 = -l2; + } +- ++ + boolean flag; +- ++ + if (d0 < 0.0D) + { + if (i1 < 70) + { + i1 = 70; + } +- ++ + if (i1 > this.worldServerInstance.getActualHeight() - 10) + { + i1 = this.worldServerInstance.getActualHeight() - 10; + } +- +- j2 = i1; +- +- for (i3 = -1; i3 <= 1; ++i3) ++ ++ j5 = i1; ++ ++ for (k2 = -1; k2 <= 1; ++k2) + { +- for (j3 = 1; j3 < 3; ++j3) ++ for (i3 = 1; i3 < 3; ++i3) + { +- for (k3 = -1; k3 < 3; ++k3) ++ for (l2 = -1; l2 < 3; ++l2) + { +- l3 = k5 + (j3 - 1) * l5 + i3 * l2; +- i4 = j2 + k3; +- j4 = k2 + (j3 - 1) * l2 - i3 * l5; +- flag = k3 < 0; +- this.worldServerInstance.setBlock(l3, i4, j4, flag ? Blocks.obsidian : Blocks.air); ++ k3 = i5 + (i3 - 1) * k5 + k2 * l5; ++ j3 = j5 + l2; ++ i4 = j2 + (i3 - 1) * l5 - k2 * k5; ++ flag = l2 < 0; ++ this.worldServerInstance.setBlock(k3, j3, i4, flag ? Blocks.obsidian : Blocks.air); + } + } + } + } +- +- for (i3 = 0; i3 < 4; ++i3) ++ ++ for (k2 = 0; k2 < 4; ++k2) + { +- for (j3 = 0; j3 < 4; ++j3) ++ for (i3 = 0; i3 < 4; ++i3) + { +- for (k3 = -1; k3 < 4; ++k3) ++ for (l2 = -1; l2 < 4; ++l2) + { +- l3 = k5 + (j3 - 1) * l5; +- i4 = j2 + k3; +- j4 = k2 + (j3 - 1) * l2; +- flag = j3 == 0 || j3 == 3 || k3 == -1 || k3 == 3; +- this.worldServerInstance.setBlock(l3, i4, j4, (Block)(flag ? Blocks.obsidian : Blocks.portal), 0, 2); ++ k3 = i5 + (i3 - 1) * k5; ++ j3 = j5 + l2; ++ i4 = j2 + (i3 - 1) * l5; ++ flag = i3 == 0 || i3 == 3 || l2 == -1 || l2 == 3; ++ this.worldServerInstance.setBlock(k3, j3, i4, flag ? Blocks.obsidian : Blocks.portal, 0, 2); + } + } +- +- for (j3 = 0; j3 < 4; ++j3) ++ ++ for (i3 = 0; i3 < 4; ++i3) + { +- for (k3 = -1; k3 < 4; ++k3) ++ for (l2 = -1; l2 < 4; ++l2) + { +- l3 = k5 + (j3 - 1) * l5; +- i4 = j2 + k3; +- j4 = k2 + (j3 - 1) * l2; +- this.worldServerInstance.notifyBlocksOfNeighborChange(l3, i4, j4, this.worldServerInstance.getBlock(l3, i4, j4)); ++ k3 = i5 + (i3 - 1) * k5; ++ j3 = j5 + l2; ++ i4 = j2 + (i3 - 1) * l5; ++ this.worldServerInstance.notifyBlocksOfNeighborChange(k3, j3, i4, this.worldServerInstance.getBlock(k3, j3, i4)); + } + } + } +- ++ + return true; + } + +- public void removeStalePortalLocations(long p_85189_1_) ++ public void removeStalePortalLocations(long par1) + { +- if (p_85189_1_ % 100L == 0L) ++ if (par1 % 100L == 0L) + { + Iterator iterator = this.destinationCoordinateKeys.iterator(); +- long j = p_85189_1_ - 600L; +- ++ long j = par1 - 600L; ++ + while (iterator.hasNext()) + { +- Long olong = (Long)iterator.next(); +- Teleporter.PortalPosition portalposition = (Teleporter.PortalPosition)this.destinationCoordinateCache.getValueByKey(olong.longValue()); +- +- if (portalposition == null || portalposition.lastUpdateTime < j) ++ Long olong = (Long) iterator.next(); ++ PortalPosition chunkcoordinatesportal = (PortalPosition) this.destinationCoordinateCache.getValueByKey(olong.longValue()); ++ ++ if (chunkcoordinatesportal == null || chunkcoordinatesportal.lastUpdateTime < j) + { + iterator.remove(); + this.destinationCoordinateCache.remove(olong.longValue()); +@@ -491,16 +645,16 @@ + } + } + } +- ++ + public class PortalPosition extends ChunkCoordinates + { + public long lastUpdateTime; + private static final String __OBFID = "CL_00000154"; +- +- public PortalPosition(int p_i1962_2_, int p_i1962_3_, int p_i1962_4_, long p_i1962_5_) ++ ++ public PortalPosition(int par2, int par3, int par4, long par5) + { +- super(p_i1962_2_, p_i1962_3_, p_i1962_4_); +- this.lastUpdateTime = p_i1962_5_; ++ super(par2, par3, par4); ++ this.lastUpdateTime = par5; + } + } + } diff --git a/patches/net/minecraft/world/World.java.patch b/patches/net/minecraft/world/World.java.patch new file mode 100644 index 0000000..8d5cbfc --- /dev/null +++ b/patches/net/minecraft/world/World.java.patch @@ -0,0 +1,1282 @@ +--- ../src-base/minecraft/net/minecraft/world/World.java ++++ ../src-work/minecraft/net/minecraft/world/World.java +@@ -23,9 +23,11 @@ + import net.minecraft.crash.CrashReport; + import net.minecraft.crash.CrashReportCategory; + import net.minecraft.entity.Entity; ++import net.minecraft.entity.EntityList; + import net.minecraft.entity.EntityLiving; + import net.minecraft.entity.player.EntityPlayer; + import net.minecraft.init.Blocks; ++import net.minecraft.item.ItemBlock; + import net.minecraft.item.ItemStack; + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.pathfinding.PathEntity; +@@ -70,6 +72,49 @@ + import net.minecraftforge.event.entity.PlaySoundAtEntityEvent; + import net.minecraft.entity.EnumCreatureType; + ++// CraftBukkit start ++import net.minecraft.entity.EntityLivingBase; ++import net.minecraft.entity.item.EntityItem; ++import net.minecraft.entity.monster.EntityGhast; ++import net.minecraft.entity.monster.EntityGolem; ++import net.minecraft.entity.monster.EntityMob; ++import net.minecraft.entity.monster.EntitySlime; ++import net.minecraft.entity.passive.EntityAnimal; ++import net.minecraft.entity.passive.EntityWaterMob; ++import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.world.gen.ChunkProviderServer; ++import org.bukkit.Bukkit; ++import org.bukkit.World.Environment; ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.util.CraftMagicNumbers; ++import org.bukkit.craftbukkit.util.LongHashSet; ++import org.bukkit.craftbukkit.util.UnsafeList; ++import org.bukkit.generator.ChunkGenerator; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.block.BlockCanBuildEvent; ++import org.bukkit.event.block.BlockPhysicsEvent; ++import org.bukkit.event.block.BlockPlaceEvent; ++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; ++import org.bukkit.event.weather.WeatherChangeEvent; ++import org.bukkit.event.weather.ThunderChangeEvent; ++// CraftBukkit end ++// Spigot Start ++import net.minecraft.entity.item.EntityXPOrb; ++import org.bukkit.craftbukkit.SpigotTimings; ++// Spigot end ++// Cauldron start ++import net.minecraft.nbt.NBTTagCompound; ++import net.minecraftforge.cauldron.CauldronHooks; ++import net.minecraftforge.cauldron.configuration.CauldronConfig; ++import net.minecraftforge.cauldron.configuration.CauldronWorldConfig; ++import net.minecraftforge.cauldron.configuration.TileEntityConfig; ++import net.minecraftforge.cauldron.configuration.TileEntityWorldConfig; ++import net.minecraftforge.common.DimensionManager; ++import org.bukkit.block.BlockState; ++// Cauldron end ++ + public abstract class World implements IBlockAccess + { + /** +@@ -83,10 +128,10 @@ + + public boolean scheduledUpdatesAreImmediate; + public List loadedEntityList = new ArrayList(); +- protected List unloadedEntityList = new ArrayList(); ++ public List unloadedEntityList = new ArrayList(); // Cauldron + public List loadedTileEntityList = new ArrayList(); + private List addedTileEntityList = new ArrayList(); +- private List field_147483_b = new ArrayList(); ++ public List field_147483_b = new ArrayList(); // Cauldron + public List playerEntities = new ArrayList(); + public List weatherEffects = new ArrayList(); + private long cloudColour = 16777215L; +@@ -100,26 +145,90 @@ + public int lastLightningBolt; + public EnumDifficulty difficultySetting; + public Random rand = new Random(); +- public final WorldProvider provider; ++ public WorldProvider provider; // CraftBukkit - remove final + protected List worldAccesses = new ArrayList(); +- protected IChunkProvider chunkProvider; ++ public IChunkProvider chunkProvider; // CraftBukkit - public + protected final ISaveHandler saveHandler; +- protected WorldInfo worldInfo; ++ public WorldInfo worldInfo; // CraftBukkit - public + public boolean findingSpawnPoint; + public MapStorage mapStorage; + public VillageCollection villageCollectionObj; + protected final VillageSiege villageSiegeObj = new VillageSiege(this); + public final Profiler theProfiler; + private final Calendar theCalendar = Calendar.getInstance(); +- protected Scoreboard worldScoreboard = new Scoreboard(); ++ public Scoreboard worldScoreboard = new Scoreboard(); // CraftBukkit - protected -> public + public boolean isRemote; +- protected Set activeChunkSet = new HashSet(); ++ // CraftBukkit start - public, longhashset ++ public boolean spawnHostileMobs; ++ public boolean spawnPeacefulMobs; ++ // Added the following ++ public long ticksPerAnimalSpawns; ++ public long ticksPerMonsterSpawns; ++ public boolean populating; ++ private int tickPosition; ++ // CraftBukkit end ++ public Set activeChunkSet = new HashSet(); // Cauldron - protected -> public + private int ambientTickCountdown; +- protected boolean spawnHostileMobs; +- protected boolean spawnPeacefulMobs; + private ArrayList collidingBoundingBoxes; + private boolean field_147481_N; + int[] lightUpdateBlockList; ++ // Cauldron start ++ public boolean restoringBlockStates = false; ++ public boolean captureBlockStates = false; ++ public boolean captureTreeGeneration = false; ++ public ArrayList capturedBlockStates = new ArrayList(); ++ public ArrayList capturedItems = new ArrayList(); ++ public int entitiesTicked; ++ public int tilesTicked; ++ public CauldronWorldConfig cauldronConfig; ++ public TileEntityWorldConfig tileentityConfig; ++ // preload world crash report classes to fix NCDFE masking StackOverflow/memory error, see #721 ++ private static boolean preloadedCrashClasses = false; ++ { ++ if (!preloadedCrashClasses) ++ { ++ // generate a temporary crash report ++ Throwable throwable = new Throwable(); ++ CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception while updating neighbours"); ++ CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being updated"); ++ ++ // loads all the required classes - including net.minecraft.crash.CallableBlockType (package private) ++ crashreportcategory.addCrashSectionCallable("Source block type", new Callable() { ++ public String call() ++ { ++ return ""; ++ } ++ }); ++ CrashReportCategory.func_147153_a(crashreportcategory, 0, 0, 0, Blocks.air, -1); ++ ++ preloadedCrashClasses = true; ++ } ++ } ++ // Cauldron end ++ // Spigot start ++ ++ /** Positions to update */ ++ protected final gnu.trove.map.hash.TLongShortHashMap activeChunkSet_CB; ++ public float growthOdds = 100; ++ protected float modifiedOdds = 100; ++ ++ public static long chunkToKey(int x, int z) ++ { ++ long k = ((((long) x) & 0xFFFF0000L) << 16) | ((((long) x) & 0x0000FFFFL) << 0); ++ k |= ((((long) z) & 0xFFFF0000L) << 32) | ((((long) z) & 0x0000FFFFL) << 16); ++ return k; ++ } ++ ++ public static int keyToX(long k) ++ { ++ return (int) (((k >> 16) & 0xFFFF0000) | (k & 0x0000FFFF)); ++ } ++ ++ public static int keyToZ(long k) ++ { ++ return (int) (((k >> 32) & 0xFFFF0000L) | ((k >> 16) & 0x0000FFFF)); ++ } ++ // Spigot end + private static final String __OBFID = "CL_00000140"; + public boolean restoringBlockSnapshots = false; + public boolean captureBlockSnapshots = false; +@@ -166,6 +275,24 @@ + return this.provider.worldChunkMgr; + } + ++ // CraftBukkit start ++ private final CraftWorld world; ++ public boolean pvpMode; ++ public boolean keepSpawnInMemory = false; // Cauldron - default to false to give forge's keepLoaded higher priority ++ public ChunkGenerator generator; ++ public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot ++ public final SpigotTimings.WorldTimingsHandler timings; // Spigot ++ ++ public CraftWorld getWorld() ++ { ++ return this.world; ++ } ++ ++ public CraftServer getServer() ++ { ++ return (CraftServer) Bukkit.getServer(); ++ } ++ + @SideOnly(Side.CLIENT) + public World(ISaveHandler p_i45368_1_, String p_i45368_2_, WorldProvider p_i45368_3_, WorldSettings p_i45368_4_, Profiler p_i45368_5_) + { +@@ -179,6 +306,12 @@ + this.worldInfo = new WorldInfo(p_i45368_4_, p_i45368_2_); + this.provider = p_i45368_3_; + perWorldStorage = new MapStorage((ISaveHandler)null); ++ // Cauldron start ++ this.world = null; ++ this.timings = null; ++ this.spigotConfig = null; ++ this.activeChunkSet_CB = null; ++ // Cauldron end + } + + // Broken up so that the WorldClient gets the chance to set the mapstorage object before the dimension initializes +@@ -207,8 +340,173 @@ + this.calculateInitialWeather(); + } + ++ // Changed signature - added gen and env ++ public World(ISaveHandler p_i45369_1_, String p_i45369_2_, WorldSettings p_i45369_3_, WorldProvider p_i45369_4_, Profiler p_i45369_5_, ChunkGenerator gen, ++ org.bukkit.World.Environment env) ++ { ++ this.spigotConfig = new org.spigotmc.SpigotWorldConfig(p_i45369_2_); // Spigot ++ // Cauldron start ++ this.cauldronConfig = new CauldronWorldConfig(p_i45369_2_, MinecraftServer.getServer().cauldronConfig); ++ this.tileentityConfig = new TileEntityWorldConfig(p_i45369_2_, MinecraftServer.getServer().tileEntityConfig); ++ // Cauldron end ++ this.worldInfo = p_i45369_1_.loadWorldInfo(); // Spigot ++ this.generator = gen; ++ this.world = new CraftWorld((WorldServer) this, gen, env); ++ this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit ++ this.ticksPerMonsterSpawns = this.getServer().getTicksPerMonsterSpawns(); // CraftBukkit ++ // CraftBukkit end ++ // Spigot start ++ this.activeChunkSet_CB = new gnu.trove.map.hash.TLongShortHashMap(spigotConfig.chunksPerTick * 5, 0.7f, Long.MIN_VALUE, Short.MIN_VALUE); ++ this.activeChunkSet_CB.setAutoCompactionFactor(0); ++ // Spigot end ++ this.ambientTickCountdown = this.rand.nextInt(12000); ++ this.spawnHostileMobs = true; ++ this.spawnPeacefulMobs = true; ++ this.collidingBoundingBoxes = new ArrayList(); ++ this.lightUpdateBlockList = new int[32768]; ++ this.saveHandler = p_i45369_1_; ++ this.theProfiler = p_i45369_5_; ++ // Cauldron start ++ // Provides a solution for different worlds getting different copies of the same data, potentially rewriting the data or causing race conditions/stale data ++ // Buildcraft has suffered from the issue this fixes. If you load the same data from two different worlds they can get two different copies of the same object, thus the last saved gets final say. ++ if (DimensionManager.getWorld(0) != null) // if overworld has loaded, use its mapstorage ++ { ++ this.mapStorage = DimensionManager.getWorld(0).mapStorage; ++ } ++ else ++ // if we are loading overworld, create a new mapstorage ++ { ++ this.mapStorage = new MapStorage(p_i45369_1_); ++ } ++ // Cauldron end ++ // this.worldInfo = p_i45369_1_.loadWorldInfo(); // Spigot - Moved up ++ ++ if (p_i45369_4_ != null) ++ { ++ this.provider = p_i45369_4_; ++ } ++ else if (this.worldInfo != null && this.worldInfo.getDimension() != 0) // Cauldron ++ { ++ this.provider = WorldProvider.getProviderForDimension(this.worldInfo.getDimension()); // Cauldron ++ } ++ else ++ { ++ this.provider = WorldProvider.getProviderForDimension(0); ++ } ++ ++ if (this.worldInfo == null) ++ { ++ this.worldInfo = new WorldInfo(p_i45369_3_, p_i45369_2_); ++ this.worldInfo.setDimension(this.provider.dimensionId); // Cauldron - Save dimension to level.dat ++ } ++ else ++ { ++ this.worldInfo.setWorldName(p_i45369_2_); ++ // Cauldron start - Use saved dimension from level.dat. Fixes issues with MultiVerse ++ if (this.worldInfo.getDimension() != 0) ++ this.provider.dimensionId = this.worldInfo.getDimension(); ++ else ++ { ++ this.worldInfo.setDimension(this.provider.dimensionId); ++ } ++ // Cauldron end ++ } ++ ++ // Cauldron start - Guarantee provider dimension is not reset. This is required for mods that rely on the provider ID to match the client dimension. Without this, IC2 will send the wrong ID to clients. ++ int providerId = this.provider.dimensionId; ++ this.provider.registerWorld(this); ++ this.provider.dimensionId = providerId; ++ // Cauldron end ++ // Cauldron start - workaround to fix TC with overworld ++ if (this.worldInfo.getDimension() == 0) ++ { ++ generator = this.getServer().getGenerator(p_i45369_2_); ++ if (generator != null) ++ { ++ getWorld().setGenerator(generator); ++ getWorld().getPopulators().addAll(generator.getDefaultPopulators(getWorld())); ++ } ++ } ++ // Cauldron end ++ this.chunkProvider = this.createChunkProvider(); ++ ++ if (this instanceof WorldServer) ++ { ++ this.perWorldStorage = new MapStorage(new WorldSpecificSaveHandler((WorldServer) this, p_i45369_1_)); ++ } ++ else ++ { ++ this.perWorldStorage = new MapStorage((ISaveHandler) null); ++ } ++ ++ timings = new SpigotTimings.WorldTimingsHandler(this); // Spigot - code below can generate new world and access timings ++ if (!this.worldInfo.isInitialized()) ++ { ++ try ++ { ++ this.initialize(p_i45369_3_); ++ } ++ catch (Throwable throwable1) ++ { ++ CrashReport crashreport = CrashReport.makeCrashReport(throwable1, "Exception initializing level"); ++ ++ try ++ { ++ this.addWorldInfoToCrashReport(crashreport); ++ } ++ catch (Throwable throwable) ++ { ++ ; ++ } ++ ++ throw new ReportedException(crashreport); ++ } ++ ++ this.worldInfo.setServerInitialized(true); ++ } ++ ++ VillageCollection villagecollection = (VillageCollection) this.perWorldStorage.loadData(VillageCollection.class, "villages"); ++ ++ if (villagecollection == null) ++ { ++ this.villageCollectionObj = new VillageCollection(this); ++ this.perWorldStorage.setData("villages", this.villageCollectionObj); ++ } ++ else ++ { ++ this.villageCollectionObj = villagecollection; ++ this.villageCollectionObj.func_82566_a(this); ++ } ++ ++ this.calculateInitialSkylight(); ++ this.calculateInitialWeather(); ++ this.getServer().addWorld(this.world); // CraftBukkit ++ } ++ + public World(ISaveHandler p_i45369_1_, String p_i45369_2_, WorldSettings p_i45369_3_, WorldProvider p_i45369_4_, Profiler p_i45369_5_) + { ++ // Cauldron start - handle dummy worlds ++ if (DimensionManager.getWorld(0) != null) ++ { ++ this.spigotConfig = new org.spigotmc.SpigotWorldConfig(p_i45369_2_); // Spigot ++ this.cauldronConfig = new CauldronWorldConfig(p_i45369_2_, MinecraftServer.getServer().cauldronConfig); ++ this.tileentityConfig = new TileEntityWorldConfig(p_i45369_2_, MinecraftServer.getServer().tileEntityConfig); ++ this.world = DimensionManager.getWorld(0).getWorld(); ++ this.timings = DimensionManager.getWorld(0).timings; ++ this.activeChunkSet_CB = new gnu.trove.map.hash.TLongShortHashMap(spigotConfig.chunksPerTick * 5, 0.7f, Long.MIN_VALUE, Short.MIN_VALUE); ++ this.activeChunkSet_CB.setAutoCompactionFactor(0); ++ this.mapStorage = DimensionManager.getWorld(0).mapStorage; ++ } ++ else ++ { ++ this.spigotConfig = null; ++ this.cauldronConfig = null; ++ this.world = null; ++ this.timings = null; ++ this.activeChunkSet_CB = null; ++ this.mapStorage = null; ++ } ++ // Cauldron end + this.ambientTickCountdown = this.rand.nextInt(12000); + this.spawnHostileMobs = true; + this.spawnPeacefulMobs = true; +@@ -216,7 +514,6 @@ + this.lightUpdateBlockList = new int[32768]; + this.saveHandler = p_i45369_1_; + this.theProfiler = p_i45369_5_; +- this.mapStorage = getMapStorage(p_i45369_1_); + this.worldInfo = p_i45369_1_.loadWorldInfo(); + + if (p_i45369_4_ != null) +@@ -235,13 +532,26 @@ + if (this.worldInfo == null) + { + this.worldInfo = new WorldInfo(p_i45369_3_, p_i45369_2_); ++ this.worldInfo.setDimension(this.provider.dimensionId); // Cauldron - Save dimension to level.dat + } + else + { + this.worldInfo.setWorldName(p_i45369_2_); ++ // Cauldron start - Use saved dimension from level.dat. Fixes issues with MultiVerse ++ if (this.worldInfo.getDimension() != 0) ++ this.provider.dimensionId = this.worldInfo.getDimension(); ++ else ++ { ++ this.worldInfo.setDimension(this.provider.dimensionId); ++ } ++ // Cauldron end + } + ++ // Cauldron start - Guarantee provider dimension is not reset. This is required for mods that rely on the provider ID to match the client dimension. Without this, IC2 will send the wrong ID to clients. ++ int providerId = this.provider.dimensionId; + this.provider.registerWorld(this); ++ this.provider.dimensionId = providerId; ++ // Cauldron end + this.chunkProvider = this.createChunkProvider(); + + if (this instanceof WorldServer) +@@ -294,6 +604,7 @@ + this.calculateInitialSkylight(); + this.calculateInitialWeather(); + } ++ // Cauldron end + + private static MapStorage s_mapStorage; + private static ISaveHandler s_savehandler; +@@ -336,6 +647,18 @@ + + public Block getBlock(int p_147439_1_, int p_147439_2_, int p_147439_3_) + { ++ // Cauldron start - tree generation ++ if (captureTreeGeneration) ++ { ++ for (BlockState blockstate : capturedBlockStates) ++ { ++ if (blockstate.getX() == p_147439_1_ && blockstate.getY() == p_147439_2_ && blockstate.getZ() == p_147439_3_) ++ { ++ return CraftMagicNumbers.getBlock(blockstate.getTypeId()); ++ } ++ } ++ } ++ // Cauldron end + if (p_147439_1_ >= -30000000 && p_147439_3_ >= -30000000 && p_147439_1_ < 30000000 && p_147439_3_ < 30000000 && p_147439_2_ >= 0 && p_147439_2_ < 256) + { + Chunk chunk = null; +@@ -404,7 +727,7 @@ + } + } + +- protected boolean chunkExists(int p_72916_1_, int p_72916_2_) ++ public boolean chunkExists(int p_72916_1_, int p_72916_2_) // Cauldron - protected -> public for repackaging + { + return this.chunkProvider.chunkExists(p_72916_1_, p_72916_2_); + } +@@ -421,6 +744,33 @@ + + public boolean setBlock(int p_147465_1_, int p_147465_2_, int p_147465_3_, Block p_147465_4_, int p_147465_5_, int p_147465_6_) + { ++ // Cauldron start - tree generation ++ if (this.captureTreeGeneration) ++ { ++ BlockState blockstate = null; ++ ++ for (BlockState previous : capturedBlockStates) ++ { ++ if (previous.getX() == p_147465_1_ && previous.getY() == p_147465_2_ && previous.getZ() == p_147465_3_) ++ { ++ blockstate = previous; ++ break; ++ } ++ } ++ if (blockstate != null) ++ { ++ capturedBlockStates.remove(blockstate); ++ } ++ else ++ { ++ blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, p_147465_1_, p_147465_2_, p_147465_3_, p_147465_6_); ++ } ++ blockstate.setTypeId(CraftMagicNumbers.getId(p_147465_4_)); ++ blockstate.setRawData((byte) p_147465_5_); ++ this.capturedBlockStates.add(blockstate); ++ return true; ++ } ++ // Cauldron end + if (p_147465_1_ >= -30000000 && p_147465_3_ >= -30000000 && p_147465_1_ < 30000000 && p_147465_3_ < 30000000) + { + if (p_147465_2_ < 0) +@@ -448,8 +798,22 @@ + this.capturedBlockSnapshots.add(blockSnapshot); + } + ++ // Cauldron start - capture blockstates ++ org.bukkit.block.BlockState blockstate = null; ++ if (this.captureBlockStates) ++ { ++ blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, p_147465_1_, p_147465_2_, p_147465_3_, p_147465_6_); ++ this.capturedBlockStates.add(blockstate); ++ } ++ + boolean flag = chunk.func_150807_a(p_147465_1_ & 15, p_147465_2_, p_147465_3_ & 15, p_147465_4_, p_147465_5_); + ++ if (!flag && this.captureBlockStates) ++ { ++ this.capturedBlockStates.remove(blockstate); ++ } ++ // Cauldron end ++ + if (!flag && blockSnapshot != null) + { + this.capturedBlockSnapshots.remove(blockSnapshot); +@@ -460,7 +824,8 @@ + this.func_147451_t(p_147465_1_, p_147465_2_, p_147465_3_); + this.theProfiler.endSection(); + +- if (flag && blockSnapshot == null) // Don't notify clients or update physics while capturing blockstates ++ // Cauldron add !this.captureBlockStates ++ if (flag && (blockSnapshot == null || !this.captureBlockStates)) // Don't notify clients or update physics while capturing blockstates + { + // Modularize client and physic updates + this.markAndNotifyBlock(p_147465_1_, p_147465_2_, p_147465_3_, chunk, block1, p_147465_4_, p_147465_6_); +@@ -496,6 +861,19 @@ + + public int getBlockMetadata(int p_72805_1_, int p_72805_2_, int p_72805_3_) + { ++ // Cauldron start - tree generation ++ if (captureTreeGeneration) ++ { ++ for (BlockState blockstate : capturedBlockStates) ++ { ++ if (blockstate.getX() == p_72805_1_ && blockstate.getY() == p_72805_2_ && blockstate.getZ() == p_72805_3_) ++ { ++ return blockstate.getRawData(); ++ } ++ } ++ } ++ // Cauldron end ++ + if (p_72805_1_ >= -30000000 && p_72805_3_ >= -30000000 && p_72805_1_ < 30000000 && p_72805_3_ < 30000000) + { + if (p_72805_2_ < 0) +@@ -610,6 +988,12 @@ + + public void notifyBlockChange(int p_147444_1_, int p_147444_2_, int p_147444_3_, Block p_147444_4_) + { ++ // CraftBukkit start ++ if (this.populating) ++ { ++ return; ++ } ++ // CraftBukkit end + this.notifyBlocksOfNeighborChange(p_147444_1_, p_147444_2_, p_147444_3_, p_147444_4_); + } + +@@ -694,6 +1078,21 @@ + + try + { ++ // CraftBukkit start ++ CraftWorld world = ((WorldServer) this).getWorld(); ++ ++ if (world != null) ++ { ++ BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(p_147460_1_, p_147460_2_, p_147460_3_), ++ CraftMagicNumbers.getId(p_147460_4_)); ++ this.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) ++ { ++ return; ++ } ++ } ++ // CraftBukkit end + block.onNeighborBlockChange(this, p_147460_1_, p_147460_2_, p_147460_3_, p_147460_4_); + } + catch (Throwable throwable1) +@@ -1307,8 +1706,14 @@ + + public boolean spawnEntityInWorld(Entity p_72838_1_) + { ++ // CraftBukkit start - Used for entities other than creatures ++ return this.addEntity(p_72838_1_, SpawnReason.DEFAULT); // Set reason as DEFAULT ++ } ++ ++ public boolean addEntity(Entity p_72838_1_, SpawnReason spawnReason) // Changed signature, added SpawnReason ++ { + // do not drop any items while restoring blocksnapshots. Prevents dupes +- if (!this.isRemote && (p_72838_1_ == null || (p_72838_1_ instanceof net.minecraft.entity.item.EntityItem && this.restoringBlockSnapshots))) return false; ++ if (!this.isRemote && (p_72838_1_ == null || (p_72838_1_ instanceof net.minecraft.entity.item.EntityItem && (this.restoringBlockSnapshots || this.restoringBlockStates)))) return false; + + int i = MathHelper.floor_double(p_72838_1_.posX / 16.0D); + int j = MathHelper.floor_double(p_72838_1_.posZ / 16.0D); +@@ -1319,8 +1724,84 @@ + flag = true; + } + ++ // CraftBukkit start ++ org.bukkit.event.Cancellable event = null; ++ // Cauldron start - workaround for handling CraftBukkit's SpawnReason with customspawners and block spawners ++ if (p_72838_1_.spawnReason != null && p_72838_1_.spawnReason.equals("natural")) ++ { ++ spawnReason = SpawnReason.NATURAL; ++ } ++ else if (p_72838_1_.spawnReason != null && p_72838_1_.spawnReason.equals("spawner")) ++ { ++ spawnReason = SpawnReason.SPAWNER; ++ } ++ // Cauldron end ++ ++ if (p_72838_1_ instanceof EntityLivingBase && !(p_72838_1_ instanceof EntityPlayerMP)) ++ { ++ // Cauldron start - add custom entity support ++ boolean isAnimal = p_72838_1_ instanceof EntityAnimal || p_72838_1_ instanceof EntityWaterMob || p_72838_1_ instanceof EntityGolem ++ || p_72838_1_.isCreatureType(EnumCreatureType.creature, false); ++ boolean isMonster = p_72838_1_ instanceof EntityMob || p_72838_1_ instanceof EntityGhast || p_72838_1_ instanceof EntitySlime ++ || p_72838_1_.isCreatureType(EnumCreatureType.monster, false); ++ // Cauldron end ++ ++ if (spawnReason != SpawnReason.CUSTOM) ++ { ++ if (isAnimal && !spawnPeacefulMobs || isMonster && !spawnHostileMobs) ++ { ++ p_72838_1_.isDead = true; ++ return false; ++ } ++ } ++ ++ event = CraftEventFactory.callCreatureSpawnEvent((EntityLivingBase) p_72838_1_, spawnReason); ++ } ++ else if (p_72838_1_ instanceof EntityItem) ++ { ++ event = CraftEventFactory.callItemSpawnEvent((EntityItem) p_72838_1_); ++ } ++ else if (p_72838_1_.getBukkitEntity() instanceof org.bukkit.entity.Projectile) ++ { ++ // Not all projectiles extend EntityProjectile, so check for Bukkit interface instead ++ event = CraftEventFactory.callProjectileLaunchEvent(p_72838_1_); ++ } ++ // Spigot start ++ else if (p_72838_1_ instanceof EntityXPOrb) ++ { ++ EntityXPOrb xp = (EntityXPOrb) p_72838_1_; ++ double radius = this.getSpigotConfig().expMerge; // Cauldron ++ ++ if (radius > 0) ++ { ++ List entities = this.getEntitiesWithinAABBExcludingEntity(p_72838_1_, p_72838_1_.boundingBox.expand(radius, radius, radius)); ++ ++ for (Entity e : entities) ++ { ++ if (e instanceof EntityXPOrb) ++ { ++ EntityXPOrb loopItem = (EntityXPOrb) e; ++ ++ if (!loopItem.isDead) ++ { ++ xp.xpValue += loopItem.xpValue; ++ loopItem.setDead(); ++ } ++ } ++ } ++ } ++ } // Spigot end ++ ++ if (event != null && (event.isCancelled() || p_72838_1_.isDead)) ++ { ++ p_72838_1_.isDead = true; ++ return false; ++ } ++ // CraftBukkit end ++ + if (!flag && !this.chunkExists(i, j)) + { ++ p_72838_1_.isDead = true; // CraftBukkit + return false; + } + else +@@ -1336,6 +1817,7 @@ + this.getChunkFromChunkCoords(i, j).addEntity(p_72838_1_); + this.loadedEntityList.add(p_72838_1_); + this.onEntityAdded(p_72838_1_); ++ net.minecraftforge.cauldron.CauldronHooks.logEntitySpawn(this, p_72838_1_, spawnReason); + return true; + } + } +@@ -1346,6 +1828,8 @@ + { + ((IWorldAccess)this.worldAccesses.get(i)).onEntityCreate(p_72923_1_); + } ++ ++ p_72923_1_.valid = true; // CraftBukkit + } + + public void onEntityRemoved(Entity p_72847_1_) +@@ -1354,6 +1838,8 @@ + { + ((IWorldAccess)this.worldAccesses.get(i)).onEntityDestroy(p_72847_1_); + } ++ ++ p_72847_1_.valid = false; // CraftBukkit + } + + public void removeEntity(Entity p_72900_1_) +@@ -1397,6 +1883,19 @@ + } + + this.loadedEntityList.remove(p_72973_1_); ++ // CraftBukkit start - Decrement loop variable field if we've already ticked this entity ++ int index = this.loadedEntityList.indexOf(p_72973_1_); ++ ++ if (index != -1) ++ { ++ if (index <= this.tickPosition) ++ { ++ this.tickPosition--; ++ } ++ ++ this.loadedEntityList.remove(index); ++ } ++ // CraftBukkit end + this.onEntityRemoved(p_72973_1_); + } + +@@ -1408,40 +1907,58 @@ + public List getCollidingBoundingBoxes(Entity p_72945_1_, AxisAlignedBB p_72945_2_) + { + this.collidingBoundingBoxes.clear(); ++ if (CauldronHooks.checkBoundingBoxSize(p_72945_1_, p_72945_2_)) return new ArrayList(); // Removing misbehaved living entities + int i = MathHelper.floor_double(p_72945_2_.minX); + int j = MathHelper.floor_double(p_72945_2_.maxX + 1.0D); + int k = MathHelper.floor_double(p_72945_2_.minY); + int l = MathHelper.floor_double(p_72945_2_.maxY + 1.0D); + int i1 = MathHelper.floor_double(p_72945_2_.minZ); + int j1 = MathHelper.floor_double(p_72945_2_.maxZ + 1.0D); ++ // Spigot start ++ int ystart = ((k - 1) < 0) ? 0 : (k - 1); + +- for (int k1 = i; k1 < j; ++k1) ++ for (int chunkx = (i >> 4); chunkx <= ((j - 1) >> 4); chunkx++) + { +- for (int l1 = i1; l1 < j1; ++l1) ++ int cx = chunkx << 4; ++ ++ for (int chunkz = (i1 >> 4); chunkz <= ((j1 - 1) >> 4); chunkz++) + { +- if (this.blockExists(k1, 64, l1)) ++ if (!this.chunkExists(chunkx, chunkz)) + { +- for (int i2 = k - 1; i2 < l; ++i2) +- { +- Block block; ++ continue; ++ } + +- if (k1 >= -30000000 && k1 < 30000000 && l1 >= -30000000 && l1 < 30000000) ++ int cz = chunkz << 4; ++ Chunk chunk = this.getChunkFromChunkCoords(chunkx, chunkz); ++ // Compute ranges within chunk ++ int xstart = (i < cx) ? cx : i; ++ int xend = (j < (cx + 16)) ? j : (cx + 16); ++ int zstart = (i1 < cz) ? cz : i1; ++ int zend = (j1 < (cz + 16)) ? j1 : (cz + 16); ++ ++ // Loop through blocks within chunk ++ for (int x = xstart; x < xend; x++) ++ { ++ for (int z = zstart; z < zend; z++) ++ { ++ for (int y = ystart; y < l; y++) + { +- block = this.getBlock(k1, i2, l1); +- } +- else +- { +- block = Blocks.stone; +- } ++ Block block = chunk.getBlock(x - cx, y, z - cz); + +- block.addCollisionBoxesToList(this, k1, i2, l1, p_72945_2_, this.collidingBoundingBoxes, p_72945_1_); ++ if (block != null) ++ { ++ block.addCollisionBoxesToList(this, x, y, z, p_72945_2_, this.collidingBoundingBoxes, p_72945_1_); ++ } ++ } + } + } + } + } ++ // Spigot end + + double d0 = 0.25D; + List list = this.getEntitiesWithinAABBExcludingEntity(p_72945_1_, p_72945_2_.expand(d0, d0, d0)); ++ net.minecraftforge.cauldron.CauldronHooks.logEntitySize(this, p_72945_1_, list); // Cauldron add logging for entity collisions + + for (int j2 = 0; j2 < list.size(); ++j2) + { +@@ -1797,11 +2314,22 @@ + Entity entity; + CrashReport crashreport; + CrashReportCategory crashreportcategory; ++ // Cauldron start ++ entitiesTicked = 0; ++ tilesTicked = 0; ++ // Cauldron end + + for (i = 0; i < this.weatherEffects.size(); ++i) + { + entity = (Entity)this.weatherEffects.get(i); + ++ // CraftBukkit start - Fixed an NPE ++ if (entity == null) ++ { ++ continue; ++ } ++ // CraftBukkit end ++ + try + { + ++entity.ticksExisted; +@@ -1862,10 +2390,13 @@ + + this.unloadedEntityList.clear(); + this.theProfiler.endStartSection("regular"); ++ org.spigotmc.ActivationRange.activateEntities(this); // Spigot ++ timings.entityTick.startTiming(); // Spigot + +- for (i = 0; i < this.loadedEntityList.size(); ++i) ++ // CraftBukkit start - Use field for loop variable ++ for (this.tickPosition = 0; this.tickPosition < this.loadedEntityList.size(); ++this.tickPosition) + { +- entity = (Entity)this.loadedEntityList.get(i); ++ entity = (Entity)this.loadedEntityList.get(this.tickPosition); + + if (entity.ridingEntity != null) + { +@@ -1884,7 +2415,9 @@ + { + try + { ++ SpigotTimings.tickEntityTimer.startTiming(); // Spigot + this.updateEntity(entity); ++ SpigotTimings.tickEntityTimer.stopTiming(); // Spigot + } + catch (Throwable throwable1) + { +@@ -1917,29 +2450,56 @@ + this.getChunkFromChunkCoords(j, l).removeEntity(entity); + } + +- this.loadedEntityList.remove(i--); ++ this.loadedEntityList.remove(this.tickPosition--); // CraftBukkit - Use field for loop variable + this.onEntityRemoved(entity); + } + + this.theProfiler.endSection(); + } + ++ timings.entityTick.stopTiming(); // Spigot + this.theProfiler.endStartSection("blockEntities"); ++ timings.tileEntityTick.startTiming(); // Spigot + this.field_147481_N = true; ++ // CraftBukkit start - From below, clean up tile entities before ticking them ++ if (!this.field_147483_b.isEmpty()) ++ { ++ for (Object tile : field_147483_b) ++ { ++ ((TileEntity) tile).onChunkUnload(); ++ } ++ this.loadedTileEntityList.removeAll(this.field_147483_b); ++ this.field_147483_b.clear(); ++ } ++ // CraftBukkit end + Iterator iterator = this.loadedTileEntityList.iterator(); + + while (iterator.hasNext()) + { + TileEntity tileentity = (TileEntity)iterator.next(); + +- if (!tileentity.isInvalid() && tileentity.hasWorldObj() && this.blockExists(tileentity.xCoord, tileentity.yCoord, tileentity.zCoord)) ++ // Spigot start ++ if (tileentity == null) + { ++ getServer().getLogger().severe("Cauldron has detected a null entity and has removed it, preventing a crash"); ++ iterator.remove(); ++ continue; ++ } ++ // Spigot end ++ ++ if (!tileentity.isInvalid() && tileentity.hasWorldObj() && CauldronHooks.canTileEntityTick(tileentity, this) ++ && this.blockExists(tileentity.xCoord, tileentity.yCoord, tileentity.zCoord)) ++ { + try + { ++ tileentity.tickTimer.startTiming(); // Spigot ++ tilesTicked++; + tileentity.updateEntity(); ++ tileentity.tickTimer.stopTiming(); // Spigot + } + catch (Throwable throwable) + { ++ tileentity.tickTimer.stopTiming(); // Spigot + crashreport = CrashReport.makeCrashReport(throwable, "Ticking block entity"); + crashreportcategory = crashreport.makeCategory("Block entity being ticked"); + tileentity.func_145828_a(crashreportcategory); +@@ -1972,6 +2532,11 @@ + } + } + ++ timings.tileEntityTick.stopTiming(); // Spigot ++ timings.tileEntityPending.startTiming(); // Spigot ++ this.field_147481_N = false; ++ ++ /* CraftBukkit start - Moved up + if (!this.field_147483_b.isEmpty()) + { + for (Object tile : field_147483_b) +@@ -1981,6 +2546,7 @@ + this.loadedTileEntityList.removeAll(this.field_147483_b); + this.field_147483_b.clear(); + } ++ */ // CraftBukkit end + + this.field_147481_N = false; + +@@ -2016,17 +2582,23 @@ + this.addedTileEntityList.clear(); + } + ++ timings.tileEntityPending.stopTiming(); // Spigot + this.theProfiler.endSection(); + this.theProfiler.endSection(); + } + + public void func_147448_a(Collection p_147448_1_) + { +- List dest = field_147481_N ? addedTileEntityList : loadedTileEntityList; +- for(TileEntity entity : (Collection)p_147448_1_) ++ // Cauldron start ++ Collection dest = field_147481_N ? addedTileEntityList : loadedTileEntityList; // List -> Collection for CB loadedTileEntityList type change ++ for(Object entity : p_147448_1_) + { +- if(entity.canUpdate()) dest.add(entity); ++ if (CauldronHooks.canUpdate((TileEntity) entity)) ++ { ++ dest.add(entity); ++ } + } ++ // Cauldron end + } + + public void updateEntity(Entity p_72870_1_) +@@ -2041,16 +2613,27 @@ + boolean isForced = getPersistentChunks().containsKey(new ChunkCoordIntPair(i >> 4, j >> 4)); + byte b0 = isForced ? (byte)0 : 32; + boolean canUpdate = !p_72866_2_ || this.checkChunksExist(i - b0, 0, j - b0, i + b0, 0, j + b0); ++ boolean forceUpdate = false; // Cauldron + + if (!canUpdate) + { + EntityEvent.CanUpdate event = new EntityEvent.CanUpdate(p_72866_1_); + MinecraftForge.EVENT_BUS.post(event); + canUpdate = event.canUpdate; ++ forceUpdate = canUpdate; // Cauldron + } +- ++ // Spigot start ++ if (!isForced && !forceUpdate && !org.spigotmc.ActivationRange.checkIfActive(p_72866_1_)) // Cauldron - ignore if forge event forced update or entity is in forced chunk ++ { ++ p_72866_1_.ticksExisted++; ++ p_72866_1_.inactiveTick(); ++ return; ++ } ++ // Spigot end + if (canUpdate) + { ++ p_72866_1_.tickTimer.startTiming(); ++ entitiesTicked++; // Cauldron + p_72866_1_.lastTickPosX = p_72866_1_.posX; + p_72866_1_.lastTickPosY = p_72866_1_.posY; + p_72866_1_.lastTickPosZ = p_72866_1_.posZ; +@@ -2134,6 +2717,7 @@ + p_72866_1_.riddenByEntity = null; + } + } ++ p_72866_1_.tickTimer.stopTiming(); // Spigot + } + } + +@@ -2570,7 +3154,7 @@ + return; + } + +- if (p_147455_4_.canUpdate()) ++ if (CauldronHooks.canUpdate(p_147455_4_)) + { + if (this.field_147481_N) + { +@@ -2718,7 +3302,15 @@ + + if (i <= 0) + { +- this.worldInfo.setThundering(!this.worldInfo.isThundering()); ++ // CraftBukkit start ++ ThunderChangeEvent thunder = new ThunderChangeEvent(this.getWorld(), !this.worldInfo.isThundering()); ++ this.getServer().getPluginManager().callEvent(thunder); ++ ++ if (!thunder.isCancelled()) ++ { ++ this.worldInfo.setThundering(!this.worldInfo.isThundering()); ++ } ++ // CraftBukkit end + } + } + +@@ -2754,7 +3346,15 @@ + + if (j <= 0) + { +- this.worldInfo.setRaining(!this.worldInfo.isRaining()); ++ // CraftBukkit start ++ WeatherChangeEvent weather = new WeatherChangeEvent(this.getWorld(), !this.worldInfo.isRaining()); ++ this.getServer().getPluginManager().callEvent(weather); ++ ++ if (!weather.isCancelled()) ++ { ++ this.worldInfo.setRaining(!this.worldInfo.isRaining()); ++ } ++ // CraftBukkit end + } + } + +@@ -2777,8 +3377,41 @@ + protected void setActivePlayerChunksAndCheckLight() + { + this.activeChunkSet.clear(); ++ // Cauldron start - add persistent chunks to be ticked for growth ++ this.activeChunkSet_CB.clear(); ++ for (ChunkCoordIntPair chunk : getPersistentChunks().keySet()) ++ { ++ this.activeChunkSet.add(chunk); ++ long key = chunkToKey(chunk.chunkXPos, chunk.chunkZPos); ++ this.activeChunkSet_CB.put(key, (short) 0); ++ if (!this.chunkExists(chunk.chunkXPos, chunk.chunkZPos)) ++ { ++ ((WorldServer) this).theChunkProviderServer.loadChunk(chunk.chunkXPos, chunk.chunkZPos); ++ } ++ } ++ // Cauldron end + this.theProfiler.startSection("buildList"); +- this.activeChunkSet.addAll(getPersistentChunks().keySet()); ++ // Spigot start ++ int optimalChunks = this.getSpigotConfig().chunksPerTick; // Cauldron ++ ++ // Quick conditions to allow us to exist early ++ if (optimalChunks <= 0) // Cauldron tick chunks even if no players are logged in ++ { ++ return; ++ } ++ ++ // Keep chunks with growth inside of the optimal chunk range ++ int chunksPerPlayer = Math.min(200, Math.max(1, (int) (((optimalChunks - playerEntities.size()) / (double) playerEntities.size()) + 0.5))); ++ // Cauldron start - use server view distance instead of capping it at 7 ++ int randRange = this.func_152379_p(); ++ if (randRange < 1) ++ { ++ throw new IllegalArgumentException("Too small view radius! edit spigot.yml and change view-distance to a value > 0."); ++ } ++ // Cauldron end ++ // odds of growth happening vs growth happening in vanilla ++ this.growthOdds = this.modifiedOdds = Math.max(35, Math.min(100, ((chunksPerPlayer + 1) * 100F) / 15F)); ++ // Spigot end + int i; + EntityPlayer entityplayer; + int j; +@@ -2788,17 +3421,28 @@ + for (i = 0; i < this.playerEntities.size(); ++i) + { + entityplayer = (EntityPlayer)this.playerEntities.get(i); +- j = MathHelper.floor_double(entityplayer.posX / 16.0D); +- k = MathHelper.floor_double(entityplayer.posZ / 16.0D); +- l = this.func_152379_p(); ++ int chunkX = MathHelper.floor_double(entityplayer.posX / 16.0D); ++ int chunkZ = MathHelper.floor_double(entityplayer.posZ / 16.0D); ++ // Spigot start - Always update the chunk the player is on ++ long key = chunkToKey(chunkX, chunkZ); ++ int existingPlayers = Math.max(0, activeChunkSet_CB.get(key)); //filter out -1's ++ activeChunkSet_CB.put(key, (short) (existingPlayers + 1)); ++ activeChunkSet.add(new ChunkCoordIntPair(chunkX, chunkZ)); // Cauldron - vanilla compatibility + +- for (int i1 = -l; i1 <= l; ++i1) ++ // Check and see if we update the chunks surrounding the player this tick ++ for (int chunk = 0; chunk < chunksPerPlayer; chunk++) + { +- for (int j1 = -l; j1 <= l; ++j1) ++ int dx = (rand.nextBoolean() ? 1 : -1) * rand.nextInt(randRange); ++ int dz = (rand.nextBoolean() ? 1 : -1) * rand.nextInt(randRange); ++ long hash = chunkToKey(dx + chunkX, dz + chunkZ); ++ ++ if (!activeChunkSet_CB.contains(hash) && this.chunkExists(dx + chunkX, dz + chunkZ)) + { +- this.activeChunkSet.add(new ChunkCoordIntPair(i1 + j, j1 + k)); ++ activeChunkSet_CB.put(hash, (short) -1); //no players ++ activeChunkSet.add(new ChunkCoordIntPair(dx + chunkX, dz + chunkZ)); // Cauldron - vanilla compatibility + } + } ++ // Spigot End + } + + this.theProfiler.endSection(); +@@ -2810,7 +3454,7 @@ + + this.theProfiler.startSection("playerCheckLight"); + +- if (!this.playerEntities.isEmpty()) ++ if (this.getSpigotConfig().randomLightUpdates && !this.playerEntities.isEmpty()) // Spigot // Cauldron + { + i = this.rand.nextInt(this.playerEntities.size()); + entityplayer = (EntityPlayer)this.playerEntities.get(i); +@@ -3284,8 +3928,21 @@ + { + Entity entity = (Entity)this.loadedEntityList.get(j); + +- if ((!(entity instanceof EntityLiving) || !((EntityLiving)entity).isNoDespawnRequired()) && p_72907_1_.isAssignableFrom(entity.getClass())) ++ // CraftBukkit start - Split out persistent check, don't apply it to special persistent mobs ++ if (entity instanceof EntityLiving) + { ++ EntityLiving entityliving = (EntityLiving) entity; ++ ++ if (entityliving.canDespawn_CB() && entityliving.isNoDespawnRequired()) ++ { ++ continue; ++ } ++ } ++ ++ if (p_72907_1_.isAssignableFrom(entity.getClass())) ++ { ++ // if ((!(entity instanceof EntityLiving) || !((EntityLiving)entity).isNoDespawnRequired()) && p_72907_1_.isAssignableFrom(entity.getClass())) ++ // CraftBukkit end + ++i; + } + } +@@ -3314,8 +3971,17 @@ + public boolean canPlaceEntityOnSide(Block p_147472_1_, int p_147472_2_, int p_147472_3_, int p_147472_4_, boolean p_147472_5_, int p_147472_6_, Entity p_147472_7_, ItemStack p_147472_8_) + { + Block block1 = this.getBlock(p_147472_2_, p_147472_3_, p_147472_4_); ++ if (block1 == null) return false; // Cauldron + AxisAlignedBB axisalignedbb = p_147472_5_ ? null : p_147472_1_.getCollisionBoundingBoxFromPool(this, p_147472_2_, p_147472_3_, p_147472_4_); +- return axisalignedbb != null && !this.checkNoEntityCollision(axisalignedbb, p_147472_7_) ? false : (block1.getMaterial() == Material.circuits && p_147472_1_ == Blocks.anvil ? true : block1.isReplaceable(this, p_147472_2_, p_147472_3_, p_147472_4_) && p_147472_1_.canReplace(this, p_147472_2_, p_147472_3_, p_147472_4_, p_147472_6_, p_147472_8_)); ++ // CraftBukkit start - store default return ++ boolean defaultReturn = axisalignedbb != null && !this.checkNoEntityCollision(axisalignedbb, p_147472_7_) ? false ++ : (block1.getMaterial() == Material.circuits && p_147472_1_ == Blocks.anvil ? true : block1.isReplaceable(this, p_147472_2_, p_147472_3_, ++ p_147472_4_) && p_147472_1_.canReplace(this, p_147472_2_, p_147472_3_, p_147472_4_, p_147472_6_, p_147472_8_)); ++ BlockCanBuildEvent event = new BlockCanBuildEvent(this.getWorld().getBlockAt(p_147472_2_, p_147472_3_, p_147472_4_), ++ CraftMagicNumbers.getId(p_147472_1_), defaultReturn); ++ this.getServer().getPluginManager().callEvent(event); ++ return event.isBuildable(); ++ // CraftBukkit end + } + + public PathEntity getPathEntityToEntity(Entity p_72865_1_, Entity p_72865_2_, float p_72865_3_, boolean p_72865_4_, boolean p_72865_5_, boolean p_72865_6_, boolean p_72865_7_) +@@ -3464,6 +4130,12 @@ + for (int i = 0; i < this.playerEntities.size(); ++i) + { + EntityPlayer entityplayer1 = (EntityPlayer)this.playerEntities.get(i); ++ // CraftBukkit start - Fixed an NPE ++ if (entityplayer1 == null || entityplayer1.isDead) ++ { ++ continue; ++ } ++ // CraftBukkit end + double d5 = entityplayer1.getDistanceSq(p_72977_1_, p_72977_3_, p_72977_5_); + + if ((p_72977_7_ < 0.0D || d5 < p_72977_7_ * p_72977_7_) && (d4 == -1.0D || d5 < d4)) +@@ -3489,7 +4161,12 @@ + for (int i = 0; i < this.playerEntities.size(); ++i) + { + EntityPlayer entityplayer1 = (EntityPlayer)this.playerEntities.get(i); +- ++ // CraftBukkit start - Fixed an NPE ++ if (entityplayer1 == null || entityplayer1.isDead) ++ { ++ continue; ++ } ++ // CraftBukkit end + if (!entityplayer1.capabilities.disableDamage && entityplayer1.isEntityAlive()) + { + double d5 = entityplayer1.getDistanceSq(p_72846_1_, p_72846_3_, p_72846_5_); +@@ -3660,6 +4337,18 @@ + + public void updateAllPlayersSleepingFlag() {} + ++ // CraftBukkit start ++ // Calls the method that checks to see if players are sleeping ++ // Called by CraftPlayer.setPermanentSleeping() ++ public void checkSleepStatus() ++ { ++ if (!this.isRemote) ++ { ++ this.updateAllPlayersSleepingFlag(); ++ } ++ } ++ // CraftBukkit end ++ + public float getWeightedThunderStrength(float p_72819_1_) + { + return (this.prevThunderingStrength + (this.thunderingStrength - this.prevThunderingStrength) * p_72819_1_) * this.getRainStrength(p_72819_1_); +@@ -3932,8 +4621,8 @@ + */ + public void addTileEntity(TileEntity entity) + { +- List dest = field_147481_N ? addedTileEntityList : loadedTileEntityList; +- if(entity.canUpdate()) ++ Collection dest = field_147481_N ? addedTileEntityList : loadedTileEntityList; // Cauldron - List -> Collection for CB loadedTileEntityList type change ++ if (CauldronHooks.canUpdate(entity)) + { + dest.add(entity); + } +@@ -4029,4 +4718,60 @@ + } + return count; + } ++ ++ // Cauldron start ++ public boolean isEmpty(int x, int y, int z) // Required until SS inheritance bug is fixed ++ { ++ return isAirBlock(x, y, z); ++ } ++ ++ public Block getType(int x, int y, int z) // Required until SS inheritance bug is fixed ++ { ++ return getBlock(x, y, z); ++ } ++ ++ public boolean isActiveChunk(int x, int z) ++ { ++ return getPersistentChunks().containsKey(new ChunkCoordIntPair(x, z)) || activeChunkSet_CB.containsKey(chunkToKey(x, z)); ++ } ++ ++ public boolean isActiveChunk(long key) ++ { ++ return isActiveChunk(keyToX(key), keyToZ(key)); ++ } ++ ++ public boolean isActiveBlockCoord(int x, int z) ++ { ++ return isActiveChunk(x >> 4, z >> 4); ++ } ++ ++ public boolean inActiveChunk(Entity entity) ++ { ++ return isActiveBlockCoord(MathHelper.floor_double(entity.posX), MathHelper.floor_double(entity.posZ)); ++ } ++ ++ // this method is used by ForgeMultipart and Immibis's Microblocks ++ public boolean canPlaceMultipart(Block block, int x, int y, int z) ++ { ++ BlockPlaceEvent placeEvent = null; ++ if (ItemStack.currentPlayer != null) ++ { ++ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(this, ItemStack.currentPlayer, ++ org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, x, y, z, 3), x, y, z); ++ } ++ ++ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) ++ { ++ return false; ++ } ++ ++ return true; ++ } ++ ++ public org.spigotmc.SpigotWorldConfig getSpigotConfig() ++ { ++ if (this.spigotConfig == null) if (DimensionManager.getWorld(0) != null) return DimensionManager.getWorld(0).spigotConfig; ++ return this.spigotConfig; ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/world/WorldManager.java.patch b/patches/net/minecraft/world/WorldManager.java.patch new file mode 100644 index 0000000..308e0d9 --- /dev/null +++ b/patches/net/minecraft/world/WorldManager.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/world/WorldManager.java ++++ ../src-work/minecraft/net/minecraft/world/WorldManager.java +@@ -12,7 +12,7 @@ + public class WorldManager implements IWorldAccess + { + private MinecraftServer mcServer; +- private WorldServer theWorldServer; ++ public WorldServer theWorldServer; // CraftBukkit - private -> public + private static final String __OBFID = "CL_00001433"; + + public WorldManager(MinecraftServer p_i1517_1_, WorldServer p_i1517_2_) diff --git a/patches/net/minecraft/world/WorldServer.java.patch b/patches/net/minecraft/world/WorldServer.java.patch new file mode 100644 index 0000000..8748918 --- /dev/null +++ b/patches/net/minecraft/world/WorldServer.java.patch @@ -0,0 +1,607 @@ +--- ../src-base/minecraft/net/minecraft/world/WorldServer.java ++++ ../src-work/minecraft/net/minecraft/world/WorldServer.java +@@ -67,11 +67,25 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import net.minecraft.block.ITileEntityProvider; ++import net.minecraft.block.BlockJukebox; ++import net.minecraft.tileentity.*; ++import org.bukkit.WeatherType; ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.util.LongHash; ++ ++import org.bukkit.event.block.BlockFormEvent; ++import org.bukkit.event.weather.LightningStrikeEvent; ++import org.bukkit.event.weather.ThunderChangeEvent; ++import org.bukkit.event.weather.WeatherChangeEvent; ++// CraftBukkit end ++ + public class WorldServer extends World + { + private static final Logger logger = LogManager.getLogger(); + private final MinecraftServer mcServer; +- private final EntityTracker theEntityTracker; ++ public EntityTracker theEntityTracker; // CraftBukkit - private final -> public + private final PlayerManager thePlayerManager; + private Set pendingTickListEntriesHashSet; + private TreeSet pendingTickListEntriesTreeSet; +@@ -92,9 +106,13 @@ + protected Set doneChunks = new HashSet(); + public List customTeleporters = new ArrayList(); + ++ // CraftBukkit start ++ public final int dimension; ++ + public WorldServer(MinecraftServer p_i45284_1_, ISaveHandler p_i45284_2_, String p_i45284_3_, int p_i45284_4_, WorldSettings p_i45284_5_, Profiler p_i45284_6_) + { + super(p_i45284_2_, p_i45284_3_, p_i45284_5_, WorldProvider.getProviderForDimension(p_i45284_4_), p_i45284_6_); ++ this.dimension = p_i45284_4_; + this.mcServer = p_i45284_1_; + this.theEntityTracker = new EntityTracker(this); + this.thePlayerManager = new PlayerManager(this); +@@ -124,6 +142,47 @@ + this.mapStorage.setData("scoreboard", scoreboardsavedata); + } + ++ scoreboardsavedata.func_96499_a(this.worldScoreboard); ++ ((ServerScoreboard) this.worldScoreboard).func_96547_a(scoreboardsavedata); ++ } ++ ++ // Add env and gen to constructor ++ public WorldServer(MinecraftServer p_i45284_1_, ISaveHandler p_i45284_2_, String p_i45284_3_, int p_i45284_4_, WorldSettings p_i45284_5_, ++ Profiler p_i45284_6_, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) ++ { ++ super(p_i45284_2_, p_i45284_3_, p_i45284_5_, WorldProvider.getProviderForDimension(p_i45284_4_), p_i45284_6_, gen, env); ++ this.dimension = p_i45284_4_; ++ this.pvpMode = p_i45284_1_.isPVPEnabled(); ++ // CraftBukkit end ++ this.mcServer = p_i45284_1_; ++ this.theEntityTracker = new EntityTracker(this); ++ this.thePlayerManager = new PlayerManager(this, spigotConfig.viewDistance); // Spigot ++ ++ if (this.entityIdMap == null) ++ { ++ this.entityIdMap = new IntHashMap(); ++ } ++ ++ if (this.pendingTickListEntriesHashSet == null) ++ { ++ this.pendingTickListEntriesHashSet = new HashSet(); ++ } ++ ++ if (this.pendingTickListEntriesTreeSet == null) ++ { ++ this.pendingTickListEntriesTreeSet = new TreeSet(); ++ } ++ ++ this.worldTeleporter = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit ++ this.worldScoreboard = new ServerScoreboard(p_i45284_1_); ++ ScoreboardSaveData scoreboardsavedata = (ScoreboardSaveData) this.mapStorage.loadData(ScoreboardSaveData.class, "scoreboard"); ++ ++ if (scoreboardsavedata == null) ++ { ++ scoreboardsavedata = new ScoreboardSaveData(); ++ this.mapStorage.setData("scoreboard", scoreboardsavedata); ++ } ++ + if (!(this instanceof WorldServerMulti)) //Forge: We fix the global mapStorage, which causes us to share scoreboards early. So don't associate the save data with the temporary scoreboard + { + scoreboardsavedata.func_96499_a(this.worldScoreboard); +@@ -132,6 +191,31 @@ + DimensionManager.setWorld(p_i45284_4_, this); + } + ++ public WorldServer(MinecraftServer minecraftServer, ISaveHandler saveHandler, String par2String, WorldProvider provider, WorldSettings par4WorldSettings, ++ Profiler theProfiler) ++ { ++ super(saveHandler, par2String, provider, par4WorldSettings, theProfiler); ++ this.dimension = provider.dimensionId; ++ this.pvpMode = minecraftServer.isPVPEnabled(); ++ this.mcServer = minecraftServer; ++ this.theEntityTracker = null; ++ this.thePlayerManager = null; ++ this.worldTeleporter = null; ++ } ++ ++ private boolean canSpawn(int x, int z) ++ { ++ if (this.generator != null) ++ { ++ return this.generator.canSpawn(this.getWorld(), x, z); ++ } ++ else ++ { ++ return this.provider.canCoordinateBeSpawn(x, z); ++ } ++ } ++ // CraftBukkit end ++ + public void tick() + { + super.tick(); +@@ -155,12 +239,19 @@ + } + + this.theProfiler.startSection("mobSpawner"); ++ // CraftBukkit start - Only call spawner if we have players online and the world allows for mobs or animals ++ long time = this.worldInfo.getWorldTotalTime(); + +- if (this.getGameRules().getGameRuleBooleanValue("doMobSpawning")) ++ if (this.getGameRules().getGameRuleBooleanValue("doMobSpawning") && (this.spawnHostileMobs || this.spawnPeacefulMobs) && (this instanceof WorldServer && this.playerEntities.size() > 0)) + { +- this.animalSpawner.findChunksForSpawning(this, this.spawnHostileMobs, this.spawnPeacefulMobs, this.worldInfo.getWorldTotalTime() % 400L == 0L); ++ timings.mobSpawn.startTiming(); // Spigot ++ this.animalSpawner.findChunksForSpawning(this, this.spawnHostileMobs ++ && (this.ticksPerMonsterSpawns != 0 && time % this.ticksPerMonsterSpawns == 0L), this.spawnPeacefulMobs ++ && (this.ticksPerAnimalSpawns != 0 && time % this.ticksPerAnimalSpawns == 0L), this.worldInfo.getWorldTotalTime() % 400L == 0L); ++ timings.mobSpawn.stopTiming(); // Spigot + } +- ++ // CraftBukkit end ++ timings.doChunkUnload.startTiming(); // Spigot + this.theProfiler.endStartSection("chunkSource"); + this.chunkProvider.unloadQueuedChunks(); + int j = this.calculateSkylightSubtracted(1.0F); +@@ -177,23 +268,40 @@ + this.worldInfo.setWorldTime(this.worldInfo.getWorldTime() + 1L); + } + ++ timings.doChunkUnload.stopTiming(); // Spigot + this.theProfiler.endStartSection("tickPending"); ++ timings.doTickPending.startTiming(); // Spigot + this.tickUpdates(false); ++ timings.doChunkUnload.stopTiming(); // Spigot + this.theProfiler.endStartSection("tickBlocks"); ++ timings.doTickTiles.startTiming(); // Spigot + this.func_147456_g(); ++ timings.doTickTiles.stopTiming(); // Spigot + this.theProfiler.endStartSection("chunkMap"); ++ timings.doChunkMap.startTiming(); // Spigot + this.thePlayerManager.updatePlayerInstances(); ++ timings.doChunkMap.stopTiming(); // Spigot + this.theProfiler.endStartSection("village"); ++ timings.doVillages.startTiming(); // Spigot + this.villageCollectionObj.tick(); + this.villageSiegeObj.tick(); ++ timings.doVillages.stopTiming(); // Spigot + this.theProfiler.endStartSection("portalForcer"); ++ timings.doPortalForcer.startTiming(); // Spigot + this.worldTeleporter.removeStalePortalLocations(this.getTotalWorldTime()); + for (Teleporter tele : customTeleporters) + { + tele.removeStalePortalLocations(getTotalWorldTime()); + } ++ timings.doPortalForcer.stopTiming(); // Spigot + this.theProfiler.endSection(); ++ timings.doSounds.startTiming(); // Spigot + this.func_147488_Z(); ++ timings.doSounds.stopTiming(); // Spigot ++ ++ timings.doChunkGC.startTiming(); // Spigot ++ this.getWorld().processChunkGC(); // CraftBukkit ++ timings.doChunkGC.stopTiming(); // Spigot + } + + public BiomeGenBase.SpawnListEntry spawnRandomCreature(EnumCreatureType p_73057_1_, int p_73057_2_, int p_73057_3_, int p_73057_4_) +@@ -212,7 +320,7 @@ + { + EntityPlayer entityplayer = (EntityPlayer)iterator.next(); + +- if (!entityplayer.isPlayerSleeping()) ++ if (!entityplayer.isPlayerSleeping() && !entityplayer.fauxSleeping) // CraftBukkit + { + this.allPlayersSleeping = false; + break; +@@ -240,7 +348,25 @@ + + private void resetRainAndThunder() + { +- provider.resetRainAndThunder(); ++ // CraftBukkit start ++ WeatherChangeEvent weather = new WeatherChangeEvent(this.getWorld(), false); ++ this.getServer().getPluginManager().callEvent(weather); ++ ThunderChangeEvent thunder = new ThunderChangeEvent(this.getWorld(), false); ++ this.getServer().getPluginManager().callEvent(thunder); ++ ++ if (!weather.isCancelled()) ++ { ++ this.worldInfo.setRainTime(0); ++ this.worldInfo.setRaining(false); ++ } ++ ++ if (!thunder.isCancelled()) ++ { ++ this.worldInfo.setThunderTime(0); ++ this.worldInfo.setThundering(false); ++ } ++ // CraftBukkit end ++ if (!weather.isCancelled() && !thunder.isCancelled()) provider.resetRainAndThunder(); // Cauldron + } + + public boolean areAllPlayersAsleep() +@@ -248,19 +374,26 @@ + if (this.allPlayersSleeping && !this.isRemote) + { + Iterator iterator = this.playerEntities.iterator(); ++ // CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers ++ boolean foundActualSleepers = false; + EntityPlayer entityplayer; + + do + { + if (!iterator.hasNext()) + { +- return true; ++ return foundActualSleepers; // CraftBukkit + } + + entityplayer = (EntityPlayer)iterator.next(); ++ // CraftBukkit start ++ if (entityplayer.isPlayerFullyAsleep()) ++ { ++ foundActualSleepers = true; ++ } + } +- while (entityplayer.isPlayerFullyAsleep()); +- ++ while (entityplayer.isPlayerFullyAsleep() || entityplayer.fauxSleeping); ++ // CraftBukkit end + return false; + } + else +@@ -302,15 +435,29 @@ + super.func_147456_g(); + int i = 0; + int j = 0; +- Iterator iterator = this.activeChunkSet.iterator(); ++ // Iterator iterator = this.activeChunkSet.iterator(); + +- while (iterator.hasNext()) ++ // Spigot start ++ for (gnu.trove.iterator.TLongShortIterator iter = activeChunkSet_CB.iterator(); iter.hasNext();) + { +- ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair)iterator.next(); +- int k = chunkcoordintpair.chunkXPos * 16; +- int l = chunkcoordintpair.chunkZPos * 16; ++ iter.advance(); ++ long chunkCoord = iter.key(); ++ int chunkX = World.keyToX(chunkCoord); ++ int chunkZ = World.keyToZ(chunkCoord); ++ // If unloaded, or in process of being unloaded, drop it ++ if ((!this.chunkExists(chunkX, chunkZ)) || (this.theChunkProviderServer.chunksToUnload.contains(chunkX, chunkZ))) ++ { ++ activeChunkSet.remove(new ChunkCoordIntPair(chunkX, chunkZ)); // Cauldron - vanilla compatibility ++ iter.remove(); ++ continue; ++ } ++ // Spigot end ++ // ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next(); ++ int k = chunkX * 16; ++ int l = chunkZ * 16; ++ + this.theProfiler.startSection("getChunk"); +- Chunk chunk = this.getChunkFromChunkCoords(chunkcoordintpair.chunkXPos, chunkcoordintpair.chunkZPos); ++ Chunk chunk = this.getChunkFromChunkCoords(chunkX, chunkZ); + this.func_147467_a(k, l, chunk); + this.theProfiler.endStartSection("tickChunk"); + chunk.func_150804_b(false); +@@ -346,12 +493,32 @@ + + if (this.isBlockFreezableNaturally(j1 + k, l1 - 1, k1 + l)) + { +- this.setBlock(j1 + k, l1 - 1, k1 + l, Blocks.ice); ++ // CraftBukkit start ++ BlockState blockState = this.getWorld().getBlockAt(j1 + k, l1 - 1, k1 + l).getState(); ++ blockState.setTypeId(Block.getIdFromBlock(Blocks.ice)); ++ BlockFormEvent iceBlockForm = new BlockFormEvent(blockState.getBlock(), blockState); ++ this.getServer().getPluginManager().callEvent(iceBlockForm); ++ ++ if (!iceBlockForm.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } + + if (this.isRaining() && this.func_147478_e(j1 + k, l1, k1 + l, true)) + { +- this.setBlock(j1 + k, l1, k1 + l, Blocks.snow_layer); ++ // CraftBukkit start ++ BlockState blockState = this.getWorld().getBlockAt(j1 + k, l1, k1 + l).getState(); ++ blockState.setTypeId(Block.getIdFromBlock(Blocks.snow_layer)); ++ BlockFormEvent snow = new BlockFormEvent(blockState.getBlock(), blockState); ++ this.getServer().getPluginManager().callEvent(snow); ++ ++ if (!snow.isCancelled()) ++ { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } + + if (this.isRaining()) +@@ -388,6 +555,7 @@ + if (block.getTickRandomly()) + { + ++i; ++ this.growthOdds = (iter.value() < 1) ? this.modifiedOdds : 100; // Spigot - grow fast if no players are in this chunk (value = player count) + block.updateTick(this, j2 + k, l2 + extendedblockstorage.getYLocation(), k2 + l, this.rand); + } + } +@@ -396,6 +564,13 @@ + + this.theProfiler.endSection(); + } ++ // Spigot Start ++ if (this.getSpigotConfig().clearChunksOnTick) // Cauldron ++ { ++ activeChunkSet_CB.clear(); ++ activeChunkSet.clear(); // Cauldron ++ } ++ // Spigot End + } + + public boolean isBlockTickScheduledThisTick(int p_147477_1_, int p_147477_2_, int p_147477_3_, Block p_147477_4_) +@@ -474,7 +649,7 @@ + + public void updateEntities() + { +- if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty()) ++ if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty()) // Cauldron Use Forge logic here + { + if (this.updateEntityTick++ >= 1200) + { +@@ -506,7 +681,16 @@ + { + if (i > 1000) + { +- i = 1000; ++ // CraftBukkit start - If the server has too much to process over time, try to alleviate that ++ if (i > 20 * 1000) ++ { ++ i = i / 20; ++ } ++ else ++ { ++ i = 1000; ++ } ++ // CraftBukkit end + } + + this.theProfiler.startSection("cleaning"); +@@ -651,7 +835,37 @@ + protected IChunkProvider createChunkProvider() + { + IChunkLoader ichunkloader = this.saveHandler.getChunkLoader(this.provider); +- this.theChunkProviderServer = new ChunkProviderServer(this, ichunkloader, this.provider.createChunkGenerator()); ++ // Cauldron start - if provider is vanilla, proceed to create a bukkit compatible chunk generator ++ if (this.provider.getClass().toString().length() <= 3 || this.provider.getClass().toString().contains("net.minecraft")) ++ { ++ // CraftBukkit start ++ org.bukkit.craftbukkit.generator.InternalChunkGenerator gen; ++ ++ if (this.generator != null) ++ { ++ gen = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, this.getSeed(), this.generator); ++ } ++ else if (this.provider instanceof WorldProviderHell) ++ { ++ gen = new org.bukkit.craftbukkit.generator.NetherChunkGenerator(this, this.getSeed()); ++ } ++ else if (this.provider instanceof WorldProviderEnd) ++ { ++ gen = new org.bukkit.craftbukkit.generator.SkyLandsChunkGenerator(this, this.getSeed()); ++ } ++ else ++ { ++ gen = new org.bukkit.craftbukkit.generator.NormalChunkGenerator(this, this.getSeed()); ++ } ++ this.theChunkProviderServer = new ChunkProviderServer(this, ichunkloader, gen); ++ // CraftBukkit end ++ } ++ else ++ // custom provider, load normally for forge compatibility ++ { ++ this.theChunkProviderServer = new ChunkProviderServer(this, ichunkloader, this.provider.createChunkGenerator()); ++ } ++ // Cauldron end + return this.theChunkProviderServer; + } + +@@ -659,29 +873,31 @@ + { + ArrayList arraylist = new ArrayList(); + +- for(int x = (p_147486_1_ >> 4); x <= (p_147486_4_ >> 4); x++) ++ // CraftBukkit start - Get tile entities from chunks instead of world ++ for (int chunkX = (p_147486_1_ >> 4); chunkX <= ((p_147486_4_ - 1) >> 4); chunkX++) + { +- for(int z = (p_147486_3_ >> 4); z <= (p_147486_6_ >> 4); z++) ++ for (int chunkZ = (p_147486_3_ >> 4); chunkZ <= ((p_147486_6_ - 1) >> 4); chunkZ++) + { +- Chunk chunk = getChunkFromChunkCoords(x, z); +- if (chunk != null) ++ Chunk chunk = getChunkFromChunkCoords(chunkX, chunkZ); ++ ++ if (chunk == null) + { +- for(Object obj : chunk.chunkTileEntityMap.values()) ++ continue; ++ } ++ ++ for (Object te : chunk.chunkTileEntityMap.values()) ++ { ++ TileEntity tileentity = (TileEntity) te; ++ ++ if ((tileentity.xCoord >= p_147486_1_) && (tileentity.yCoord >= p_147486_2_) && (tileentity.zCoord >= p_147486_3_) ++ && (tileentity.xCoord < p_147486_4_) && (tileentity.yCoord < p_147486_5_) && (tileentity.zCoord < p_147486_6_)) + { +- TileEntity entity = (TileEntity)obj; +- if (!entity.isInvalid()) +- { +- if (entity.xCoord >= p_147486_1_ && entity.yCoord >= p_147486_2_ && entity.zCoord >= p_147486_3_ && +- entity.xCoord <= p_147486_4_ && entity.yCoord <= p_147486_5_ && entity.zCoord <= p_147486_6_) +- { +- arraylist.add(entity); +- } +- } ++ arraylist.add(tileentity); + } + } + } + } +- ++ // CraftBukkit end + return arraylist; + } + +@@ -733,7 +949,28 @@ + int i = 0; + int j = this.provider.getAverageGroundLevel(); + int k = 0; ++ // CraftBukkit start ++ if (this.generator != null) ++ { ++ Random rand = new Random(this.getSeed()); ++ org.bukkit.Location spawn = this.generator.getFixedSpawnLocation(((WorldServer) this).getWorld(), rand); + ++ if (spawn != null) ++ { ++ if (spawn.getWorld() != ((WorldServer) this).getWorld()) ++ { ++ throw new IllegalStateException("Cannot set spawn point for " + this.worldInfo.getWorldName() + " to be in another world (" ++ + spawn.getWorld().getName() + ")"); ++ } ++ else ++ { ++ this.worldInfo.setSpawnPosition(spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ()); ++ this.findingSpawnPoint = false; ++ return; ++ } ++ } ++ } ++ // CraftBukkit end + if (chunkposition != null) + { + i = chunkposition.chunkPosX; +@@ -876,6 +1113,20 @@ + + public boolean addWeatherEffect(Entity p_72942_1_) + { ++ // Cauldron start - vanilla compatibility ++ if (p_72942_1_ instanceof net.minecraft.entity.effect.EntityLightningBolt) ++ { ++ // CraftBukkit start ++ LightningStrikeEvent lightning = new LightningStrikeEvent(this.getWorld(), (org.bukkit.entity.LightningStrike) p_72942_1_.getBukkitEntity()); ++ this.getServer().getPluginManager().callEvent(lightning); ++ ++ if (lightning.isCancelled()) ++ { ++ return false; ++ } ++ // CraftBukkit end ++ } ++ // Cauldron end + if (super.addWeatherEffect(p_72942_1_)) + { + this.mcServer.getConfigurationManager().sendToAllNear(p_72942_1_.posX, p_72942_1_.posY, p_72942_1_.posZ, 512.0D, this.provider.dimensionId, new S2CPacketSpawnGlobalEntity(p_72942_1_)); +@@ -894,13 +1145,23 @@ + + public Explosion newExplosion(Entity p_72885_1_, double p_72885_2_, double p_72885_4_, double p_72885_6_, float p_72885_8_, boolean p_72885_9_, boolean p_72885_10_) + { ++ // CraftBukkit start ++ Explosion explosion = super.newExplosion(p_72885_1_, p_72885_2_, p_72885_4_, p_72885_6_, p_72885_8_, p_72885_9_, p_72885_10_); ++ ++ if (explosion.wasCanceled) ++ { ++ return explosion; ++ } ++ ++ /* Remove + Explosion explosion = new Explosion(this, p_72885_1_, p_72885_2_, p_72885_4_, p_72885_6_, p_72885_8_); + explosion.isFlaming = p_72885_9_; + explosion.isSmoking = p_72885_10_; + if (net.minecraftforge.event.ForgeEventFactory.onExplosionStart(this, explosion)) return explosion; + explosion.doExplosionA(); + explosion.doExplosionB(false); +- ++ */ ++ // CraftBukkit end - TODO: Check if explosions are still properly implemented + if (!p_72885_10_) + { + explosion.affectedBlockPositions.clear(); +@@ -977,7 +1238,7 @@ + { + boolean flag = this.isRaining(); + super.updateWeather(); +- ++ /* CraftBukkit start + if (this.prevRainingStrength != this.rainingStrength) + { + this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(7, this.rainingStrength), this.provider.dimensionId); +@@ -988,10 +1249,6 @@ + this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(8, this.thunderingStrength), this.provider.dimensionId); + } + +- /*The function in use here has been replaced in order to only send the weather info to players in the correct dimension, +- rather than to all players on the server. This is what causes the client-side rain, as the +- client believes that it has started raining locally, rather than in another dimension. +- */ + if (flag != this.isRaining()) + { + if (flag) +@@ -1006,6 +1263,20 @@ + this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(7, this.rainingStrength), this.provider.dimensionId); + this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(8, this.thunderingStrength), this.provider.dimensionId); + } ++ // */ ++ if (flag != this.isRaining()) ++ { ++ // Only send weather packets to those affected ++ for (int i = 0; i < this.playerEntities.size(); ++i) ++ { ++ if (((EntityPlayerMP) this.playerEntities.get(i)).worldObj == this) ++ { ++ ((EntityPlayerMP) this.playerEntities.get(i)).setPlayerWeather((!flag ? WeatherType.DOWNFALL : WeatherType.CLEAR), false); ++ } ++ } ++ ++ // CraftBukkit end ++ } + } + + protected int func_152379_p() +@@ -1069,4 +1340,31 @@ + this(); + } + } ++ ++ // CraftBukkit start - Compatibility methods for BlockChangeDelegate ++ public boolean setRawTypeId(int x, int y, int z, int typeId) ++ { ++ return this.setBlock(x, y, z, Block.getBlockById(typeId), 0, 4); ++ } ++ ++ public boolean setRawTypeIdAndData(int x, int y, int z, int typeId, int data) ++ { ++ return this.setBlock(x, y, z, Block.getBlockById(typeId), data, 4); ++ } ++ ++ public boolean setTypeId(int x, int y, int z, int typeId) ++ { ++ return this.setBlock(x, y, z, Block.getBlockById(typeId), 0, 3); ++ } ++ ++ public boolean setTypeIdAndData(int x, int y, int z, int typeId, int data) ++ { ++ return this.setBlock(x, y, z, Block.getBlockById(typeId), data, 3); ++ } ++ ++ public int getTypeId(int x, int y, int z) ++ { ++ return Block.getIdFromBlock(getBlock(x, y, z)); ++ } ++ // CraftBukkit end + } diff --git a/patches/net/minecraft/world/WorldServerMulti.java.patch b/patches/net/minecraft/world/WorldServerMulti.java.patch new file mode 100644 index 0000000..7382cf4 --- /dev/null +++ b/patches/net/minecraft/world/WorldServerMulti.java.patch @@ -0,0 +1,36 @@ +--- ../src-base/minecraft/net/minecraft/world/WorldServerMulti.java ++++ ../src-work/minecraft/net/minecraft/world/WorldServerMulti.java +@@ -9,6 +9,17 @@ + { + private static final String __OBFID = "CL_00001430"; + ++ // CraftBukkit start - Add Environment and ChunkGenerator arguments ++ public WorldServerMulti(MinecraftServer p_i45283_1_, ISaveHandler p_i45283_2_, String p_i45283_3_, int p_i45283_4_, WorldSettings p_i45283_5_, WorldServer p_i45283_6_, Profiler p_i45283_7_, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) ++ { ++ super(p_i45283_1_, p_i45283_2_, p_i45283_3_, p_i45283_4_, p_i45283_5_, p_i45283_7_, env, gen); ++ // CraftBukkit end ++ this.mapStorage = p_i45283_6_.mapStorage; ++ this.worldScoreboard = p_i45283_6_.getScoreboard(); ++ //this.worldInfo = new DerivedWorldInfo(p_i45283_6_.getWorldInfo()); ++ } ++ ++ // Cauldron start - vanilla compatibility + public WorldServerMulti(MinecraftServer p_i45283_1_, ISaveHandler p_i45283_2_, String p_i45283_3_, int p_i45283_4_, WorldSettings p_i45283_5_, WorldServer p_i45283_6_, Profiler p_i45283_7_) + { + super(p_i45283_1_, p_i45283_2_, p_i45283_3_, p_i45283_4_, p_i45283_5_, p_i45283_7_); +@@ -16,9 +27,15 @@ + this.worldScoreboard = p_i45283_6_.getScoreboard(); + this.worldInfo = new DerivedWorldInfo(p_i45283_6_.getWorldInfo()); + } ++ // Cauldron end + ++ /* we handle all saving including perWorldStorage in WorldServer.saveLevel. This needs to be disabled since we follow ++ // bukkit's world saving methods by using a seperate save handler for each world. Each world folder needs to generate a corresponding ++ // level.dat for plugins that require it such as MultiWorld. + protected void saveLevel() throws MinecraftException + { + this.perWorldStorage.saveAllData(); + } ++ */ ++ // Cauldron end + } diff --git a/patches/net/minecraft/world/WorldType.java.patch b/patches/net/minecraft/world/WorldType.java.patch new file mode 100644 index 0000000..3fcd419 --- /dev/null +++ b/patches/net/minecraft/world/WorldType.java.patch @@ -0,0 +1,23 @@ +--- ../src-base/minecraft/net/minecraft/world/WorldType.java ++++ ../src-work/minecraft/net/minecraft/world/WorldType.java +@@ -19,6 +19,7 @@ + import net.minecraft.world.gen.layer.GenLayerZoom; + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; ++import net.minecraftforge.common.util.EnumHelper; // Cauldron + + public class WorldType + { +@@ -49,6 +50,12 @@ + this.canBeCreated = true; + this.worldTypeId = p_i1960_1_; + worldTypes[p_i1960_1_] = this; ++ // Cauldron start - add worldtype for bukkit if it does not already exist ++ if (org.bukkit.WorldType.getByName(p_i1960_2_) == null) ++ { ++ EnumHelper.addBukkitWorldType(p_i1960_2_); ++ } ++ // Cauldron end + } + + public String getWorldTypeName() diff --git a/patches/net/minecraft/world/biome/BiomeDecorator.java.patch b/patches/net/minecraft/world/biome/BiomeDecorator.java.patch new file mode 100644 index 0000000..43a53c9 --- /dev/null +++ b/patches/net/minecraft/world/biome/BiomeDecorator.java.patch @@ -0,0 +1,164 @@ +--- ../src-base/minecraft/net/minecraft/world/biome/BiomeDecorator.java ++++ ../src-work/minecraft/net/minecraft/world/biome/BiomeDecorator.java +@@ -24,6 +24,14 @@ + import net.minecraftforge.common.*; + import net.minecraftforge.event.terraingen.*; + ++// Spigot Start ++import java.util.ArrayList; ++import java.util.Iterator; ++import java.util.List; ++ ++import net.minecraft.world.chunk.Chunk; ++// Spigot End ++ + public class BiomeDecorator + { + public World currentWorld; +@@ -61,6 +69,7 @@ + public int clayPerChunk; + public int bigMushroomsPerChunk; + public boolean generateLakes; ++ private final List chunksToUnload = new ArrayList(); // Spigot + private static final String __OBFID = "CL_00000164"; + + public BiomeDecorator() +@@ -194,7 +203,7 @@ + { + k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); ++ i1 = nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + WorldGenerator worldgenerator = p_150513_1_.getRandomWorldGenForGrass(this.randomGenerator); + worldgenerator.generate(this.currentWorld, this.randomGenerator, k, i1, l); + } +@@ -204,7 +213,7 @@ + { + k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); ++ i1 = nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + (new WorldGenDeadBush(Blocks.deadbush)).generate(this.currentWorld, this.randomGenerator, k, i1, l); + } + +@@ -214,7 +223,7 @@ + k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; + +- for (i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); i1 > 0 && this.currentWorld.isAirBlock(k, i1 - 1, l); --i1) ++ for (i1 = nextInt(this.getHighestBlockYAt(k, l) * 2); i1 > 0 && this.currentWorld.isAirBlock(k, i1 - 1, l); --i1) // Spigot + { + ; + } +@@ -229,7 +238,7 @@ + { + k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- i1 = this.currentWorld.getHeightValue(k, l); ++ i1 = this.getHighestBlockYAt(k, l); // Spigot + this.mushroomBrownGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); + } + +@@ -237,7 +246,7 @@ + { + k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); ++ i1 = nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + this.mushroomRedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); + } + } +@@ -246,7 +255,7 @@ + { + j = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- l = nextInt(this.currentWorld.getHeightValue(j, k) * 2); ++ l = nextInt(this.getHighestBlockYAt(j, k) * 2); // Spigot + this.mushroomBrownGen.generate(this.currentWorld, this.randomGenerator, j, l, k); + } + +@@ -254,7 +263,7 @@ + { + j = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- l = nextInt(this.currentWorld.getHeightValue(j, k) * 2); ++ l = nextInt(this.getHighestBlockYAt(j, k) * 2); // Spigot + this.mushroomRedGen.generate(this.currentWorld, this.randomGenerator, j, l, k); + } + +@@ -263,7 +272,7 @@ + { + k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); ++ i1 = nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + this.reedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); + } + +@@ -271,7 +280,7 @@ + { + k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); ++ i1 = nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + this.reedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); + } + +@@ -280,7 +289,7 @@ + { + j = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- l = nextInt(this.currentWorld.getHeightValue(j, k) * 2); ++ l = nextInt(this.getHighestBlockYAt(j, k) * 2); // Spigot + (new WorldGenPumpkin()).generate(this.currentWorld, this.randomGenerator, j, l, k); + } + +@@ -289,7 +298,7 @@ + { + k = this.chunk_X + this.randomGenerator.nextInt(16) + 8; + l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8; +- i1 = nextInt(this.currentWorld.getHeightValue(k, l) * 2); ++ i1 = nextInt(this.getHighestBlockYAt(k, l) * 2); // Spigot + this.cactusGen.generate(this.currentWorld, this.randomGenerator, k, i1, l); + } + +@@ -313,6 +322,7 @@ + } + } + ++ this.unloadChunks(); // Spigot - unload chunks we force loaded + MinecraftForge.EVENT_BUS.post(new DecorateBiomeEvent.Post(currentWorld, randomGenerator, chunk_X, chunk_Z)); + } + +@@ -360,6 +370,31 @@ + MinecraftForge.ORE_GEN_BUS.post(new OreGenEvent.Post(currentWorld, randomGenerator, chunk_X, chunk_Z)); + } + ++ // Spigot start - force load chunks ++ private int getHighestBlockYAt(int i, int j) ++ { ++ // Make sure the chunk is loaded ++ if (!this.currentWorld.chunkExists(i >> 4, j >> 4)) ++ { ++ // If not, load it, then add it to our unload list ++ this.chunksToUnload.add(this.currentWorld.getChunkFromChunkCoords(i >> 4, j >> 4)); ++ } ++ ++ return this.currentWorld.getHeightValue(i, j); ++ } ++ ++ private void unloadChunks() ++ { ++ Iterator iter = this.chunksToUnload.iterator(); ++ ++ while (iter.hasNext()) ++ { ++ this.currentWorld.getWorld().unloadChunk(iter.next().bukkitChunk); ++ iter.remove(); ++ } ++ } ++ // Spigot end ++ + private int nextInt(int i) { + if (i <= 1) + return 0; diff --git a/patches/net/minecraft/world/chunk/Chunk.java.patch b/patches/net/minecraft/world/chunk/Chunk.java.patch new file mode 100644 index 0000000..bc3a65f --- /dev/null +++ b/patches/net/minecraft/world/chunk/Chunk.java.patch @@ -0,0 +1,266 @@ +--- ../src-base/minecraft/net/minecraft/world/chunk/Chunk.java ++++ ../src-work/minecraft/net/minecraft/world/chunk/Chunk.java +@@ -36,6 +36,17 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import net.minecraft.block.BlockContainer; ++import org.bukkit.Bukkit; ++// CraftBukkit end ++// Spigot start ++import net.minecraft.entity.EntityLiving; ++import net.minecraft.entity.EnumCreatureType; ++import net.minecraft.entity.player.EntityPlayerMP; ++import net.minecraft.inventory.IInventory; ++// Spigot end ++ + public class Chunk + { + private static final Logger logger = LogManager.getLogger(); +@@ -62,6 +73,8 @@ + public int heightMapMinimum; + public long inhabitedTime; + private int queuedLightChecks; ++ public gnu.trove.map.hash.TObjectIntHashMap entityCount = new gnu.trove.map.hash.TObjectIntHashMap(); // Spigot (Cauldron protected -> public) ++ public int lastAccessedTick; // Cauldron track last time the chunk was accessed + private static final String __OBFID = "CL_00000373"; + + public Chunk(World p_i1995_1_, int p_i1995_2_, int p_i1995_3_) +@@ -80,13 +93,22 @@ + + for (int k = 0; k < this.entityLists.length; ++k) + { +- this.entityLists[k] = new ArrayList(); ++ this.entityLists[k] = new org.bukkit.craftbukkit.util.UnsafeList(); // CraftBukkit - ArrayList -> UnsafeList + } + + Arrays.fill(this.precipitationHeightMap, -999); + Arrays.fill(this.blockBiomeArray, (byte) - 1); ++ // CraftBukkit start ++ if (!(this instanceof EmptyChunk)) ++ { ++ this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); ++ } + } + ++ public org.bukkit.Chunk bukkitChunk; ++ public boolean mustSave; ++ // CraftBukkit end ++ + public Chunk(World p_i45446_1_, Block[] p_i45446_2_, int p_i45446_3_, int p_i45446_4_) + { + this(p_i45446_1_, p_i45446_3_, p_i45446_4_); +@@ -589,9 +611,10 @@ + + if (!this.worldObj.isRemote) + { ++ if (block1 == null) return false; // Cauldron + block1.onBlockPreDestroy(this.worldObj, l1, p_150807_2_, i2, k1); + } +- ++ // Cauldron - Removed CB patch that fixes BUKKIT-5238 to prevent stackoverflows. See issue #1165 and #1169 + extendedblockstorage.func_150818_a(p_150807_1_, p_150807_2_ & 15, p_150807_3_, p_150807_4_); + extendedblockstorage.setExtBlockMetadata(p_150807_1_, p_150807_2_ & 15, p_150807_3_, p_150807_5_); // This line duplicates the one below, so breakBlock fires with valid worldstate + +@@ -777,8 +800,14 @@ + + if (i != this.xPosition || j != this.zPosition) + { +- logger.warn("Wrong location! " + p_76612_1_ + " (at " + i + ", " + j + " instead of " + this.xPosition + ", " + this.zPosition + ")"); +- Thread.dumpStack(); ++ // CraftBukkit start ++ Bukkit.getLogger().warning("Wrong location for " + p_76612_1_ + " in world '" + worldObj.getWorld().getName() + "'!"); ++ //logger.warn("Wrong location! " + p_76612_1_ + " (at " + i + ", " + j + " instead of " + this.xPosition + ", " + this.zPosition + ")"); ++ //Thread.dumpStack(); ++ Bukkit.getLogger().warning( ++ "Entity is at " + p_76612_1_.posX + "," + p_76612_1_.posZ + " (chunk " + i + "," + j + ") but was stored in chunk " + this.xPosition + "," ++ + this.zPosition); ++ // CraftBukkit end + } + + int k = MathHelper.floor_double(p_76612_1_.posY / 16.0D); +@@ -799,6 +828,26 @@ + p_76612_1_.chunkCoordY = k; + p_76612_1_.chunkCoordZ = this.zPosition; + this.entityLists[k].add(p_76612_1_); ++ // Spigot start - increment creature type count ++ // Keep this synced up with World.a(Class) ++ if (p_76612_1_ instanceof EntityLiving) ++ { ++ EntityLiving entityliving = (EntityLiving) p_76612_1_; ++ ++ if (entityliving.canDespawn_CB() && entityliving.isNoDespawnRequired()) ++ { ++ return; ++ } ++ } ++ ++ for (EnumCreatureType creatureType : EnumCreatureType.values()) ++ { ++ if (creatureType.getCreatureClass().isAssignableFrom(p_76612_1_.getClass())) ++ { ++ this.entityCount.adjustOrPutValue(creatureType.getCreatureClass(), 1, 1); ++ } ++ } ++ // Spigot end + } + + public void removeEntity(Entity p_76622_1_) +@@ -819,6 +868,26 @@ + } + + this.entityLists[p_76608_2_].remove(p_76608_1_); ++ // Spigot start - decrement creature type count ++ // Keep this synced up with World.a(Class) ++ if (p_76608_1_ instanceof EntityLiving) ++ { ++ EntityLiving entityliving = (EntityLiving) p_76608_1_; ++ ++ if (entityliving.canDespawn_CB() && entityliving.isNoDespawnRequired()) ++ { ++ return; ++ } ++ } ++ ++ for (EnumCreatureType creatureType : EnumCreatureType.values()) ++ { ++ if (creatureType.getCreatureClass().isAssignableFrom(p_76608_1_.getClass())) ++ { ++ this.entityCount.adjustValue(creatureType.getCreatureClass(), -1); ++ } ++ } ++ // Spigot end + } + + public boolean canBlockSeeTheSky(int p_76619_1_, int p_76619_2_, int p_76619_3_) +@@ -874,9 +943,23 @@ + p_150812_4_.xCoord = this.xPosition * 16 + p_150812_1_; + p_150812_4_.yCoord = p_150812_2_; + p_150812_4_.zCoord = this.zPosition * 16 + p_150812_3_; ++ // Cauldron start - validate TE for corrupted data ++ Block block = null; ++ try ++ { ++ block = this.getBlock(p_150812_1_, p_150812_2_, p_150812_3_); ++ } ++ catch (ArrayIndexOutOfBoundsException e) ++ { ++ System.out.println("ERROR: Detected corrupted TileEntity " + p_150812_4_ + " with bad extended block ID of " ++ + ((p_150812_2_ & 15) << 8 | p_150812_3_ << 4 | p_150812_1_) + " @ " + p_150812_4_.xCoord + ", " + p_150812_4_.yCoord + ", " ++ + p_150812_4_.zCoord + ". Removing TE to avoid crash..."); ++ p_150812_4_.invalidate(); ++ return; ++ } + + int metadata = getBlockMetadata(p_150812_1_, p_150812_2_, p_150812_3_); +- if (this.getBlock(p_150812_1_, p_150812_2_, p_150812_3_).hasTileEntity(metadata)) ++ if (block != null && block.hasTileEntity(metadata)) // Cauldron end + { + if (this.chunkTileEntityMap.containsKey(chunkposition)) + { +@@ -886,6 +969,16 @@ + p_150812_4_.validate(); + this.chunkTileEntityMap.put(chunkposition, p_150812_4_); + } ++ // CraftBukkit start ++ else if (net.minecraft.server.MinecraftServer.getServer().tileEntityConfig.enableTEPlaceWarning.getValue()) // Cauldron ++ { ++ System.out.println("Attempted to place a tile entity (" + p_150812_4_ + ") at " + p_150812_4_.xCoord + "," + p_150812_4_.yCoord + "," ++ + p_150812_4_.zCoord + " (" + org.bukkit.Material.getMaterial(Block.getIdFromBlock(getBlock(p_150812_1_, p_150812_2_, p_150812_3_))) ++ + ") where there was no entity tile!"); ++ System.out.println("Chunk coordinates: " + (this.xPosition * 16) + "," + (this.zPosition * 16)); ++ new Exception().printStackTrace(); ++ } ++ // CraftBukkit end + } + + public void removeTileEntity(int p_150805_1_, int p_150805_2_, int p_150805_3_) +@@ -936,6 +1029,21 @@ + + for (int i = 0; i < this.entityLists.length; ++i) + { ++ // CraftBukkit start ++ java.util.Iterator iter = this.entityLists[i].iterator(); ++ ++ while (iter.hasNext()) ++ { ++ Entity entity = (Entity) iter.next(); ++ ++ // Do not pass along players, as doing so can get them stuck outside of time. ++ // (which for example disables inventory icon updates and prevents block breaking) ++ if (entity instanceof EntityPlayerMP) ++ { ++ iter.remove(); ++ } ++ } ++ // CraftBukkit end + this.worldObj.unloadEntities(this.entityLists[i]); + } + MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(this)); +@@ -1035,6 +1143,7 @@ + + public void populateChunk(IChunkProvider p_76624_1_, IChunkProvider p_76624_2_, int p_76624_3_, int p_76624_4_) + { ++ worldObj.timings.syncChunkLoadPostTimer.startTiming(); // Spigot + if (!this.isTerrainPopulated && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_ + 1) && p_76624_1_.chunkExists(p_76624_3_, p_76624_4_ + 1) && p_76624_1_.chunkExists(p_76624_3_ + 1, p_76624_4_)) + { + p_76624_1_.populate(p_76624_2_, p_76624_3_, p_76624_4_); +@@ -1054,6 +1163,7 @@ + { + p_76624_1_.populate(p_76624_2_, p_76624_3_ - 1, p_76624_4_ - 1); + } ++ worldObj.timings.syncChunkLoadPostTimer.stopTiming(); // Spigot + } + + public int getPrecipitationHeight(int p_76626_1_, int p_76626_2_) +@@ -1184,8 +1294,10 @@ + if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) + { + nibblearray = this.storageArrays[l].getMetadataArray(); +- System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); +- k += nibblearray.data.length; ++ // Spigot start ++ System.arraycopy(p_76607_1_, k, nibblearray.getValueArray(), 0, nibblearray.getValueArray().length); ++ k += nibblearray.getValueArray().length; ++ // Spigot end + } + } + +@@ -1194,8 +1306,10 @@ + if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) + { + nibblearray = this.storageArrays[l].getBlocklightArray(); +- System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); +- k += nibblearray.data.length; ++ // Spigot start ++ System.arraycopy(p_76607_1_, k, nibblearray.getValueArray(), 0, nibblearray.getValueArray().length); ++ k += nibblearray.getValueArray().length; ++ // Spigot end + } + } + +@@ -1206,8 +1320,10 @@ + if ((p_76607_2_ & 1 << l) != 0 && this.storageArrays[l] != null) + { + nibblearray = this.storageArrays[l].getSkylightArray(); +- System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); +- k += nibblearray.data.length; ++ // Spigot start ++ System.arraycopy(p_76607_1_, k, nibblearray.getValueArray(), 0, nibblearray.getValueArray().length); ++ k += nibblearray.getValueArray().length; ++ // Spigot end + } + } + } +@@ -1229,8 +1345,8 @@ + nibblearray = this.storageArrays[l].createBlockMSBArray(); + } + +- System.arraycopy(p_76607_1_, k, nibblearray.data, 0, nibblearray.data.length); +- k += nibblearray.data.length; ++ System.arraycopy(p_76607_1_, k, nibblearray.getValueArray(), 0, nibblearray.getValueArray().length); ++ k += nibblearray.getValueArray().length; + } + } + else if (p_76607_4_ && this.storageArrays[l] != null && this.storageArrays[l].getBlockMSBArray() != null) diff --git a/patches/net/minecraft/world/chunk/NibbleArray.java.patch b/patches/net/minecraft/world/chunk/NibbleArray.java.patch new file mode 100644 index 0000000..c1eef4e --- /dev/null +++ b/patches/net/minecraft/world/chunk/NibbleArray.java.patch @@ -0,0 +1,233 @@ +--- ../src-base/minecraft/net/minecraft/world/chunk/NibbleArray.java ++++ ../src-work/minecraft/net/minecraft/world/chunk/NibbleArray.java +@@ -1,47 +1,215 @@ + package net.minecraft.world.chunk; + ++import java.util.Arrays; // Spigot ++ + public class NibbleArray + { +- public final byte[] data; ++ public byte[] data; // Spigot - remove final // Cauldron - make public + private final int depthBits; + private final int depthBitsPlusFour; + private static final String __OBFID = "CL_00000371"; + +- public NibbleArray(int p_i1992_1_, int p_i1992_2_) ++ // Spigot start ++ private byte trivialValue; ++ private byte trivialByte; ++ private int length; ++ private static final int LEN2K = 2048; // Universal length used right now - optimize around this ++ private static final byte[][] TrivLen2k; ++ ++ static + { +- this.data = new byte[p_i1992_1_ >> 1]; +- this.depthBits = p_i1992_2_; +- this.depthBitsPlusFour = p_i1992_2_ + 4; ++ TrivLen2k = new byte[16][]; ++ ++ for (int i = 0; i < 16; i++) ++ { ++ TrivLen2k[i] = new byte[LEN2K]; ++ Arrays.fill(TrivLen2k[i], (byte)(i | (i << 4))); ++ } + } + +- public NibbleArray(byte[] p_i1993_1_, int p_i1993_2_) ++ // Try to convert array to trivial array ++ public void detectAndProcessTrivialArray() + { +- this.data = p_i1993_1_; +- this.depthBits = p_i1993_2_; +- this.depthBitsPlusFour = p_i1993_2_ + 4; ++ trivialValue = (byte)(data[0] & 0xF); ++ trivialByte = (byte)(trivialValue | (trivialValue << 4)); ++ ++ for (int i = 0; i < data.length; i++) ++ { ++ if (data[i] != trivialByte) ++ { ++ return; ++ } ++ } ++ ++ // All values matches, so array is trivial ++ this.length = data.length; ++ this.data = null; + } + +- public int get(int p_76582_1_, int p_76582_2_, int p_76582_3_) ++ // Force array to non-trivial state ++ public void forceToNonTrivialArray() + { +- int l = p_76582_2_ << this.depthBitsPlusFour | p_76582_3_ << this.depthBits | p_76582_1_; ++ if (this.data == null) ++ { ++ this.data = new byte[this.length]; ++ ++ if (this.trivialByte != 0) ++ { ++ Arrays.fill(this.data, this.trivialByte); ++ } ++ } ++ } ++ ++ // Test if array is in trivial state ++ public boolean isTrivialArray() ++ { ++ return (this.data == null); ++ } ++ ++ // Get value of all elements (only valid if array is in trivial state) ++ public int getTrivialArrayValue() ++ { ++ return this.trivialValue; ++ } ++ ++ // Get logical length of byte array for nibble data (whether trivial or non-trivial) ++ public int getByteLength() ++ { ++ if (this.data == null) ++ { ++ return this.length; ++ } ++ else ++ { ++ return this.data.length; ++ } ++ } ++ ++ // Return byte encoding of array (whether trivial or non-trivial) - returns read-only array if trivial (do not modify!) ++ public byte[] getValueArray() ++ { ++ if (this.data != null) ++ { ++ return this.data; ++ } ++ else ++ { ++ byte[] rslt; ++ ++ if (this.length == LEN2K) // All current uses are 2k long, but be safe ++ { ++ rslt = TrivLen2k[this.trivialValue]; ++ } ++ else ++ { ++ rslt = new byte[this.length]; ++ ++ if (this.trivialByte != 0) ++ { ++ Arrays.fill(rslt, this.trivialByte); ++ } ++ } ++ ++ return rslt; ++ } ++ } ++ ++ // Copy byte representation of array to given offset in given byte array ++ public int copyToByteArray(byte[] dest, int off) ++ { ++ if (this.data == null) ++ { ++ Arrays.fill(dest, off, off + this.length, this.trivialByte); ++ return off + this.length; ++ } ++ else ++ { ++ System.arraycopy(this.data, 0, dest, off, this.data.length); ++ return off + this.data.length; ++ } ++ } ++ ++ // Resize array to given byte length ++ public void resizeArray(int len) ++ { ++ if (this.data == null) ++ { ++ this.length = len; ++ } ++ else if (this.data.length != len) ++ { ++ byte[] newa = new byte[len]; ++ System.arraycopy(this.data, 0, newa, 0, ((this.data.length > len) ? len : this.data.length)); ++ this.data = newa; ++ } ++ } ++ // Spigot end ++ ++ public NibbleArray(int par1, int par2) ++ { ++ // Spigot start ++ //this.a = new byte[i >> 1]; ++ this.data = null; // Start off as trivial value (all same zero value) ++ this.length = par1 >> 1; ++ this.trivialByte = this.trivialValue = 0; ++ // Spigot end ++ this.depthBits = par2; ++ this.depthBitsPlusFour = par2 + 4; ++ } ++ ++ public NibbleArray(byte[] par1ArrayOfByte, int par2) ++ { ++ this.data = par1ArrayOfByte; ++ this.depthBits = par2; ++ this.depthBitsPlusFour = par2 + 4; ++ detectAndProcessTrivialArray(); // Spigot ++ } ++ ++ public int get(int par1, int par2, int par3) ++ { ++ if (this.data == null) ++ { ++ return this.trivialValue; // Spigot ++ } ++ ++ int l = par2 << this.depthBitsPlusFour | par3 << this.depthBits | par1; + int i1 = l >> 1; + int j1 = l & 1; + return j1 == 0 ? this.data[i1] & 15 : this.data[i1] >> 4 & 15; + } + +- public void set(int p_76581_1_, int p_76581_2_, int p_76581_3_, int p_76581_4_) ++ public void set(int par1, int par2, int par3, int par4) + { +- int i1 = p_76581_2_ << this.depthBitsPlusFour | p_76581_3_ << this.depthBits | p_76581_1_; ++ // Spigot start ++ if (this.data == null) ++ { ++ if (par4 != this.trivialValue) // Not same as trivial value, array no longer trivial ++ { ++ this.data = new byte[this.length]; ++ ++ if (this.trivialByte != 0) ++ { ++ Arrays.fill(this.data, this.trivialByte); ++ } ++ } ++ else ++ { ++ return; ++ } ++ } ++ ++ // Spigot end ++ int i1 = par2 << this.depthBitsPlusFour | par3 << this.depthBits | par1; + int j1 = i1 >> 1; + int k1 = i1 & 1; + + if (k1 == 0) + { +- this.data[j1] = (byte)(this.data[j1] & 240 | p_76581_4_ & 15); ++ this.data[j1] = (byte)(this.data[j1] & 240 | par4 & 15); + } + else + { +- this.data[j1] = (byte)(this.data[j1] & 15 | (p_76581_4_ & 15) << 4); ++ this.data[j1] = (byte)(this.data[j1] & 15 | (par4 & 15) << 4); + } + } + } diff --git a/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch b/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch new file mode 100644 index 0000000..202c464 --- /dev/null +++ b/patches/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch @@ -0,0 +1,228 @@ +--- ../src-base/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java ++++ ../src-work/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java +@@ -33,6 +33,13 @@ + import org.apache.logging.log4j.Logger; + + import cpw.mods.fml.common.FMLLog; ++// Cauldron start ++import java.util.Map; ++import net.minecraft.server.MinecraftServer; ++import net.minecraftforge.cauldron.CauldronUtils; ++import net.minecraftforge.common.util.EnumHelper; ++import cpw.mods.fml.common.asm.transformers.SideTransformer; ++// Cauldron end + + public class AnvilChunkLoader implements IChunkLoader, IThreadedFileIO + { +@@ -41,6 +48,7 @@ + private Set pendingAnvilChunksCoordinates = new HashSet(); + private Object syncLockObject = new Object(); + public final File chunkSaveLocation; ++ private List checkedTileEntities = new ArrayList(); // Cauldron + private static final String __OBFID = "CL_00000384"; + + public AnvilChunkLoader(File p_i2003_1_) +@@ -73,13 +81,16 @@ + + public Chunk loadChunk(World p_75815_1_, int p_75815_2_, int p_75815_3_) throws IOException + { ++ p_75815_1_.timings.syncChunkLoadDataTimer.startTiming(); // Spigot + Object[] data = this.loadChunk__Async(p_75815_1_, p_75815_2_, p_75815_3_); ++ p_75815_1_.timings.syncChunkLoadDataTimer.stopTiming(); // Spigot + + if (data != null) + { + Chunk chunk = (Chunk) data[0]; + NBTTagCompound nbttagcompound = (NBTTagCompound) data[1]; + this.loadEntities(p_75815_1_, nbttagcompound.getCompoundTag("Level"), chunk); ++ MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, nbttagcompound)); // Cauldron - Don't call ChunkDataEvent.Load async + return chunk; + } + +@@ -156,8 +167,8 @@ + if (!chunk.isAtLocation(p_75822_2_, p_75822_3_)) + { + logger.error("Chunk file at " + p_75822_2_ + "," + p_75822_3_ + " is in the wrong location; relocating. (Expected " + p_75822_2_ + ", " + p_75822_3_ + ", got " + chunk.xPosition + ", " + chunk.zPosition + ")"); +- p_75822_4_.setInteger("xPos", p_75822_2_); +- p_75822_4_.setInteger("zPos", p_75822_3_); ++ p_75822_4_.getCompoundTag("Level").setInteger("xPos", p_75822_2_); // CraftBukkit - .getCompound("Level") ++ p_75822_4_.getCompoundTag("Level").setInteger("zPos", p_75822_3_); // CraftBukkit - .getCompound("Level") + // Have to move tile entities since we don't load them at this stage + NBTTagList tileEntities = p_75822_4_.getCompoundTag("Level").getTagList("TileEntities", 10); + +@@ -187,8 +198,18 @@ + + public void saveChunk(World p_75816_1_, Chunk p_75816_2_) throws MinecraftException, IOException + { +- p_75816_1_.checkSessionLock(); ++ // CraftBukkit start - "handle" exception ++ try ++ { ++ p_75816_1_.checkSessionLock(); ++ } ++ catch (MinecraftException ex) ++ { ++ ex.printStackTrace(); ++ } + ++ // CraftBukkit end ++ + try + { + NBTTagCompound nbttagcompound = new NBTTagCompound(); +@@ -230,7 +251,7 @@ + + public boolean writeNextIO() + { +- AnvilChunkLoader.PendingChunk pendingchunk = null; ++ PendingChunk pendingchunktosave = null; + Object object = this.syncLockObject; + + synchronized (this.syncLockObject) +@@ -240,15 +261,15 @@ + return false; + } + +- pendingchunk = (AnvilChunkLoader.PendingChunk)this.chunksToRemove.remove(0); +- this.pendingAnvilChunksCoordinates.remove(pendingchunk.chunkCoordinate); ++ pendingchunktosave = (PendingChunk) this.chunksToRemove.remove(0); ++ this.pendingAnvilChunksCoordinates.remove(pendingchunktosave.chunkCoordinate); + } + +- if (pendingchunk != null) ++ if (pendingchunktosave != null) + { + try + { +- this.writeChunkNBTTags(pendingchunk); ++ this.writeChunkNBTTags(pendingchunktosave); + } + catch (Exception exception) + { +@@ -259,7 +280,7 @@ + return true; + } + +- private void writeChunkNBTTags(AnvilChunkLoader.PendingChunk p_75821_1_) throws IOException ++ public void writeChunkNBTTags(AnvilChunkLoader.PendingChunk p_75821_1_) throws java.io.IOException // CraftBukkit - public -> private, added throws + { + DataOutputStream dataoutputstream = RegionFileCache.getChunkOutputStream(this.chunkSaveLocation, p_75821_1_.chunkCoordinate.chunkXPos, p_75821_1_.chunkCoordinate.chunkZPos); + CompressedStreamTools.write(p_75821_1_.nbtTags, dataoutputstream); +@@ -307,19 +328,19 @@ + + if (extendedblockstorage.getBlockMSBArray() != null) + { +- nbttagcompound1.setByteArray("Add", extendedblockstorage.getBlockMSBArray().data); ++ nbttagcompound1.setByteArray("Add", extendedblockstorage.getBlockMSBArray().getValueArray()); // Spigot + } + +- nbttagcompound1.setByteArray("Data", extendedblockstorage.getMetadataArray().data); +- nbttagcompound1.setByteArray("BlockLight", extendedblockstorage.getBlocklightArray().data); ++ nbttagcompound1.setByteArray("Data", extendedblockstorage.getMetadataArray().getValueArray()); // Spigot ++ nbttagcompound1.setByteArray("BlockLight", extendedblockstorage.getBlocklightArray().getValueArray()); // Spigot + + if (flag) + { +- nbttagcompound1.setByteArray("SkyLight", extendedblockstorage.getSkylightArray().data); ++ nbttagcompound1.setByteArray("SkyLight", extendedblockstorage.getSkylightArray().getValueArray()); // Spigot + } + else + { +- nbttagcompound1.setByteArray("SkyLight", new byte[extendedblockstorage.getBlocklightArray().data.length]); ++ nbttagcompound1.setByteArray("SkyLight", new byte[extendedblockstorage.getBlocklightArray().getValueArray().length]); // Spigot + } + + nbttaglist.appendTag(nbttagcompound1); +@@ -455,6 +476,7 @@ + + public void loadEntities(World p_75823_1_, NBTTagCompound p_75823_2_, Chunk chunk) + { ++ p_75823_1_.timings.syncChunkLoadEntitiesTimer.startTiming(); // Spigot + NBTTagList nbttaglist1 = p_75823_2_.getTagList("Entities", 10); + + if (nbttaglist1 != null) +@@ -468,24 +490,31 @@ + if (entity2 != null) + { + chunk.addEntity(entity2); +- Entity entity = entity2; +- +- for (NBTTagCompound nbttagcompound2 = nbttagcompound3; nbttagcompound2.hasKey("Riding", 10); nbttagcompound2 = nbttagcompound2.getCompoundTag("Riding")) ++ // Cauldron start - check to see if we killed entity due to invalid location ++ if (!entity2.isDead) + { +- Entity entity1 = EntityList.createEntityFromNBT(nbttagcompound2.getCompoundTag("Riding"), p_75823_1_); ++ Entity entity = entity2; + +- if (entity1 != null) ++ for (NBTTagCompound nbttagcompound2 = nbttagcompound3; nbttagcompound2.hasKey("Riding", 10); nbttagcompound2 = nbttagcompound2.getCompoundTag("Riding")) + { +- chunk.addEntity(entity1); +- entity.mountEntity(entity1); +- } ++ Entity entity1 = EntityList.createEntityFromNBT(nbttagcompound2.getCompoundTag("Riding"), p_75823_1_); + +- entity = entity1; ++ if (entity1 != null) ++ { ++ chunk.addEntity(entity1); ++ entity.mountEntity(entity1); ++ } ++ ++ entity = entity1; ++ } + } ++ // Cauldron end + } + } + } + ++ p_75823_1_.timings.syncChunkLoadEntitiesTimer.stopTiming(); // Spigot ++ p_75823_1_.timings.syncChunkLoadTileEntitiesTimer.startTiming(); // Spigot + NBTTagList nbttaglist2 = p_75823_2_.getTagList("TileEntities", 10); + + if (nbttaglist2 != null) +@@ -497,11 +526,35 @@ + + if (tileentity != null) + { ++ // Cauldron start - check if TE should tick and inject into Bukkit's InventoryType ++ if (!this.checkedTileEntities.contains(tileentity.getClass())) ++ { ++ // verify if TE should tick ++ if (MinecraftServer.getServer().tileEntityConfig.preventInvalidTileEntityUpdates.getValue()) ++ { ++ SideTransformer.allowInvalidSide = true; ++ if (!CauldronUtils.isOverridingUpdateEntity(tileentity.getClass()) && CauldronUtils.canTileEntityUpdate(tileentity.getClass())) ++ { ++ if (MinecraftServer.getServer().tileEntityConfig.enableTECanUpdateWarning.getValue()) ++ { ++ MinecraftServer.getServer().logInfo("Detected TE " + tileentity.getClass().getName() + " with canUpdate set to true and no updateEntity override!. Please report to mod author as this can hurt performance."); ++ } ++ MinecraftServer.getServer().bannedTileEntityUpdates.add(tileentity.getClass()); ++ } ++ SideTransformer.allowInvalidSide = false; ++ } ++ // inject TE into InventoryType to support inventory events ++ EnumHelper.addInventoryType(tileentity); ++ this.checkedTileEntities.add(tileentity.getClass()); ++ } ++ // Cauldron end + chunk.addTileEntity(tileentity); + } + } + } + ++ p_75823_1_.timings.syncChunkLoadTileEntitiesTimer.stopTiming(); // Spigot ++ p_75823_1_.timings.syncChunkLoadTileTicksTimer.startTiming(); // Spigot + if (p_75823_2_.hasKey("TileTicks", 9)) + { + NBTTagList nbttaglist3 = p_75823_2_.getTagList("TileTicks", 10); +@@ -515,6 +568,7 @@ + } + } + } ++ p_75823_1_.timings.syncChunkLoadTileTicksTimer.stopTiming(); // Spigot + + // return chunk; + } diff --git a/patches/net/minecraft/world/chunk/storage/AnvilSaveHandler.java.patch b/patches/net/minecraft/world/chunk/storage/AnvilSaveHandler.java.patch new file mode 100644 index 0000000..816fb33 --- /dev/null +++ b/patches/net/minecraft/world/chunk/storage/AnvilSaveHandler.java.patch @@ -0,0 +1,25 @@ +--- ../src-base/minecraft/net/minecraft/world/chunk/storage/AnvilSaveHandler.java ++++ ../src-work/minecraft/net/minecraft/world/chunk/storage/AnvilSaveHandler.java +@@ -21,6 +21,11 @@ + public IChunkLoader getChunkLoader(WorldProvider p_75763_1_) + { + File file1 = this.getWorldDirectory(); ++ // Cauldron start ++ // To workaround the issue of Bukkit relying on every world having a seperate container ++ // we won't be generating a DIMXX folder for chunk loaders since this name is already generated ++ // for the world container with provider.getSaveFolder(). ++ /* + File file2; + + if (p_75763_1_.getSaveFolder() != null) +@@ -32,7 +37,9 @@ + else + { + return new AnvilChunkLoader(file1); +- } ++ } */ ++ return new AnvilChunkLoader(file1); ++ // Cauldron end + } + + public void saveWorldInfoWithPlayer(WorldInfo p_75755_1_, NBTTagCompound p_75755_2_) diff --git a/patches/net/minecraft/world/chunk/storage/ChunkLoader.java.patch b/patches/net/minecraft/world/chunk/storage/ChunkLoader.java.patch new file mode 100644 index 0000000..a2651a7 --- /dev/null +++ b/patches/net/minecraft/world/chunk/storage/ChunkLoader.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/net/minecraft/world/chunk/storage/ChunkLoader.java ++++ ../src-work/minecraft/net/minecraft/world/chunk/storage/ChunkLoader.java +@@ -113,9 +113,11 @@ + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + nbttagcompound1.setByte("Y", (byte)(j & 255)); + nbttagcompound1.setByteArray("Blocks", abyte1); +- nbttagcompound1.setByteArray("Data", nibblearray.data); +- nbttagcompound1.setByteArray("SkyLight", nibblearray1.data); +- nbttagcompound1.setByteArray("BlockLight", nibblearray2.data); ++ // Spigot start - data -> getValueArray() accessor ++ nbttagcompound1.setByteArray("Data", nibblearray.getValueArray()); ++ nbttagcompound1.setByteArray("SkyLight", nibblearray1.getValueArray()); ++ nbttagcompound1.setByteArray("BlockLight", nibblearray2.getValueArray()); ++ // Spigot end + nbttaglist.appendTag(nbttagcompound1); + } + } diff --git a/patches/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java.patch b/patches/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java.patch new file mode 100644 index 0000000..dffa033 --- /dev/null +++ b/patches/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java.patch @@ -0,0 +1,217 @@ +--- ../src-base/minecraft/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java ++++ ../src-work/minecraft/net/minecraft/world/chunk/storage/ExtendedBlockStorage.java +@@ -31,6 +31,29 @@ + } + } + ++ // CraftBukkit start ++ public ExtendedBlockStorage(int y, boolean flag, byte[] blkIds, byte[] extBlkIds) ++ { ++ this.yBase = y; ++ this.blockLSBArray = blkIds; ++ ++ if (extBlkIds != null) ++ { ++ this.blockMSBArray = new NibbleArray(extBlkIds, 4); ++ } ++ ++ this.blockMetadataArray = new NibbleArray(this.blockLSBArray.length, 4); ++ this.blocklightArray = new NibbleArray(this.blockLSBArray.length, 4); ++ ++ if (flag) ++ { ++ this.skylightArray = new NibbleArray(this.blockLSBArray.length, 4); ++ } ++ ++ this.removeInvalidBlocks(); ++ } ++ // CraftBukkit end ++ + public Block getBlockByExtId(int p_150819_1_, int p_150819_2_, int p_150819_3_) + { + int l = this.blockLSBArray[p_150819_2_ << 8 | p_150819_3_ << 4 | p_150819_1_] & 255; +@@ -139,6 +162,106 @@ + + public void removeInvalidBlocks() + { ++ // CraftBukkit start - Optimize for speed ++ byte[] blkIds = this.blockLSBArray; ++ int cntNonEmpty = 0; ++ int cntTicking = 0; ++ ++ if (this.blockMSBArray == null) // No extended block IDs? Don't waste time messing with them ++ { ++ for (int off = 0; off < blkIds.length; off++) ++ { ++ int l = blkIds[off] & 0xFF; ++ ++ if (l > 0) ++ { ++ if (Block.getBlockById(l) == null) ++ { ++ blkIds[off] = 0; ++ } ++ else ++ { ++ ++cntNonEmpty; ++ ++ if (Block.getBlockById(l).getTickRandomly()) ++ { ++ ++cntTicking; ++ } ++ } ++ } ++ } ++ } ++ else ++ { ++ this.blockMSBArray.forceToNonTrivialArray(); // Spigot ++ byte[] ext = this.blockMSBArray.getValueArray(); ++ ++ for (int off = 0, off2 = 0; off < blkIds.length;) ++ { ++ byte extid = ext[off2]; ++ int l = (blkIds[off] & 0xFF) | ((extid & 0xF) << 8); // Even data ++ ++ if (l > 0) ++ { ++ if (Block.getBlockById(l) == null) ++ { ++ blkIds[off] = 0; ++ ext[off2] &= 0xF0; ++ } ++ else ++ { ++ ++cntNonEmpty; ++ ++ if (Block.getBlockById(l).getTickRandomly()) ++ { ++ ++cntTicking; ++ } ++ } ++ } ++ ++ off++; ++ l = (blkIds[off] & 0xFF) | ((extid & 0xF0) << 4); // Odd data ++ ++ if (l > 0) ++ { ++ if (Block.getBlockById(l) == null) ++ { ++ blkIds[off] = 0; ++ ext[off2] &= 0x0F; ++ } ++ else ++ { ++ ++cntNonEmpty; ++ ++ if (Block.getBlockById(l).getTickRandomly()) ++ { ++ ++cntTicking; ++ } ++ } ++ } ++ ++ off++; ++ off2++; ++ } ++ ++ // Spigot start ++ this.blockMSBArray.detectAndProcessTrivialArray(); ++ ++ if (this.blockMSBArray.isTrivialArray() && (this.blockMSBArray.getTrivialArrayValue() == 0)) ++ { ++ this.blockMSBArray = null; ++ } ++ ++ // Spigot end ++ } ++ ++ this.blockRefCount = cntNonEmpty; ++ this.tickRefCount = cntTicking; ++ } ++ ++ public void old_recalcBlockCounts() ++ { ++ // CraftBukkit end + this.blockRefCount = 0; + this.tickRefCount = 0; + +@@ -197,29 +320,72 @@ + + public void setBlockLSBArray(byte[] p_76664_1_) + { +- this.blockLSBArray = p_76664_1_; ++ this.blockLSBArray = this.validateByteArray(p_76664_1_); // CraftBukkit - Validate data + } + + public void setBlockMSBArray(NibbleArray p_76673_1_) + { +- this.blockMSBArray = p_76673_1_; ++ // CraftBukkit start - Don't hang on to an empty nibble array ++ boolean empty = true; ++ ++ // Spigot start ++ if ((!p_76673_1_.isTrivialArray()) || (p_76673_1_.getTrivialArrayValue() != 0)) ++ { ++ empty = false; ++ } ++ ++ // Spigot end ++ ++ if (empty) ++ { ++ return; ++ } ++ ++ // CraftBukkit end ++ this.blockMSBArray = this.validateNibbleArray(p_76673_1_); // CraftBukkit - Validate data + } + + public void setBlockMetadataArray(NibbleArray p_76668_1_) + { +- this.blockMetadataArray = p_76668_1_; ++ this.blockMetadataArray = this.validateNibbleArray(p_76668_1_); // CraftBukkit - Validate data + } + + public void setBlocklightArray(NibbleArray p_76659_1_) + { +- this.blocklightArray = p_76659_1_; ++ this.blocklightArray = this.validateNibbleArray(p_76659_1_); // CraftBukkit - Validate data + } + + public void setSkylightArray(NibbleArray p_76666_1_) + { +- this.skylightArray = p_76666_1_; ++ this.skylightArray = this.validateNibbleArray(p_76666_1_); // CraftBukkit - Validate data + } + ++ // CraftBukkit start - Validate array lengths ++ private NibbleArray validateNibbleArray(NibbleArray nibbleArray) ++ { ++ // Spigot start - fix for more awesome nibble arrays ++ if (nibbleArray != null && nibbleArray.getByteLength() < 2048) ++ { ++ nibbleArray.resizeArray(2048); ++ } ++ ++ // Spigot end ++ return nibbleArray; ++ } ++ ++ private byte[] validateByteArray(byte[] byteArray) ++ { ++ if (byteArray != null && byteArray.length < 4096) ++ { ++ byte[] newArray = new byte[4096]; ++ System.arraycopy(byteArray, 0, newArray, 0, byteArray.length); ++ byteArray = newArray; ++ } ++ ++ return byteArray; ++ } ++ // CraftBukkit end ++ + @SideOnly(Side.CLIENT) + public NibbleArray createBlockMSBArray() + { diff --git a/patches/net/minecraft/world/chunk/storage/RegionFileCache.java.patch b/patches/net/minecraft/world/chunk/storage/RegionFileCache.java.patch new file mode 100644 index 0000000..5c19476 --- /dev/null +++ b/patches/net/minecraft/world/chunk/storage/RegionFileCache.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraft/world/chunk/storage/RegionFileCache.java ++++ ../src-work/minecraft/net/minecraft/world/chunk/storage/RegionFileCache.java +@@ -10,7 +10,7 @@ + + public class RegionFileCache + { +- private static final Map regionsByFilename = new HashMap(); ++ public static final Map regionsByFilename = new HashMap(); // CraftBukkit - private -> public + private static final String __OBFID = "CL_00000383"; + + public static synchronized RegionFile createOrLoadRegionFile(File p_76550_0_, int p_76550_1_, int p_76550_2_) diff --git a/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch b/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch new file mode 100644 index 0000000..38d8ec9 --- /dev/null +++ b/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch @@ -0,0 +1,465 @@ +--- ../src-base/minecraft/net/minecraft/world/gen/ChunkProviderServer.java ++++ ../src-work/minecraft/net/minecraft/world/gen/ChunkProviderServer.java +@@ -32,23 +32,40 @@ + + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; ++// CraftBukkit start ++import java.util.Random; ++import net.minecraft.block.BlockSand; ++import org.bukkit.Server; ++import org.bukkit.craftbukkit.util.LongHash; ++import org.bukkit.craftbukkit.util.LongHashSet; ++import org.bukkit.craftbukkit.util.LongObjectHashMap; ++import org.bukkit.event.world.ChunkUnloadEvent; ++// CraftBukkit end ++// Cauldron start ++import cpw.mods.fml.common.FMLCommonHandler; ++import net.minecraft.server.MinecraftServer; ++import net.minecraftforge.cauldron.configuration.CauldronConfig; ++import net.minecraftforge.cauldron.CauldronHooks; ++// Cauldron end + + public class ChunkProviderServer implements IChunkProvider + { + private static final Logger logger = LogManager.getLogger(); +- private Set chunksToUnload = Collections.newSetFromMap(new ConcurrentHashMap()); +- private Chunk defaultEmptyChunk; ++ public LongHashSet chunksToUnload = new LongHashSet(); // LongHashSet ++ public Chunk defaultEmptyChunk; + public IChunkProvider currentChunkProvider; + public IChunkLoader currentChunkLoader; +- public boolean loadChunkOnProvideRequest = true; +- public LongHashMap loadedChunkHashMap = new LongHashMap(); +- public List loadedChunks = new ArrayList(); ++ public boolean loadChunkOnProvideRequest = MinecraftServer.getServer().cauldronConfig.loadChunkOnRequest.getValue(); // Cauldron - if true, allows mods to force load chunks. to disable, set load-chunk-on-request in cauldron.yml to false ++ public int initialTick; // Cauldron counter to keep track of when this loader was created ++ public LongObjectHashMap loadedChunkHashMap = new LongObjectHashMap(); ++ public List loadedChunks = new ArrayList(); // Cauldron - vanilla compatibility + public WorldServer worldObj; + private Set loadingChunks = com.google.common.collect.Sets.newHashSet(); + private static final String __OBFID = "CL_00001436"; + + public ChunkProviderServer(WorldServer p_i1520_1_, IChunkLoader p_i1520_2_, IChunkProvider p_i1520_3_) + { ++ this.initialTick = MinecraftServer.currentTick; // Cauldron keep track of when the loader was created + this.defaultEmptyChunk = new EmptyChunk(p_i1520_1_, 0, 0); + this.worldObj = p_i1520_1_; + this.currentChunkLoader = p_i1520_2_; +@@ -57,10 +74,10 @@ + + public boolean chunkExists(int p_73149_1_, int p_73149_2_) + { +- return this.loadedChunkHashMap.containsItem(ChunkCoordIntPair.chunkXZ2Int(p_73149_1_, p_73149_2_)); ++ return this.loadedChunkHashMap.containsKey(LongHash.toLong(p_73149_1_, p_73149_2_)); // CraftBukkit + } + +- public List func_152380_a() ++ public List func_152380_a() // Vanilla compatibility + { + return this.loadedChunks; + } +@@ -74,20 +91,39 @@ + int l = p_73241_2_ * 16 + 8 - chunkcoordinates.posZ; + short short1 = 128; + ++ // CraftBukkit start + if (k < -short1 || k > short1 || l < -short1 || l > short1) + { +- this.chunksToUnload.add(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(p_73241_1_, p_73241_2_))); ++ this.chunksToUnload.add(p_73241_1_, p_73241_2_); ++ Chunk c = this.loadedChunkHashMap.get(LongHash.toLong(p_73241_1_, p_73241_2_)); ++ ++ if (c != null) ++ { ++ c.mustSave = true; ++ } ++ CauldronHooks.logChunkUnload(this, p_73241_1_, p_73241_2_, "Chunk added to unload queue"); + } ++ ++ // CraftBukkit end + } + else + { +- this.chunksToUnload.add(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(p_73241_1_, p_73241_2_))); ++ // CraftBukkit start ++ this.chunksToUnload.add(p_73241_1_, p_73241_2_); ++ Chunk c = this.loadedChunkHashMap.get(LongHash.toLong(p_73241_1_, p_73241_2_)); ++ ++ if (c != null) ++ { ++ c.mustSave = true; ++ } ++ CauldronHooks.logChunkUnload(this, p_73241_1_, p_73241_2_, "Chunk added to unload queue"); ++ // CraftBukkit end + } + } + + public void unloadAllChunks() + { +- Iterator iterator = this.loadedChunks.iterator(); ++ Iterator iterator = this.loadedChunkHashMap.values().iterator(); // CraftBukkit + + while (iterator.hasNext()) + { +@@ -103,9 +139,9 @@ + + public Chunk loadChunk(int par1, int par2, Runnable runnable) + { +- long k = ChunkCoordIntPair.chunkXZ2Int(par1, par2); +- this.chunksToUnload.remove(Long.valueOf(k)); +- Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(k); ++ this.chunksToUnload.remove(par1, par2); ++ Chunk chunk = (Chunk) this.loadedChunkHashMap.get(LongHash.toLong(par1, par2)); ++ boolean newChunk = false; + AnvilChunkLoader loader = null; + + if (this.currentChunkLoader instanceof AnvilChunkLoader) +@@ -113,6 +149,8 @@ + loader = (AnvilChunkLoader) this.currentChunkLoader; + } + ++ CauldronHooks.logChunkLoad(this, "Get", par1, par2, true); ++ + // We can only use the queue for already generated chunks + if (chunk == null && loader != null && loader.chunkExists(this.worldObj, par1, par2)) + { +@@ -142,18 +180,19 @@ + + public Chunk originalLoadChunk(int p_73158_1_, int p_73158_2_) + { +- long k = ChunkCoordIntPair.chunkXZ2Int(p_73158_1_, p_73158_2_); +- this.chunksToUnload.remove(Long.valueOf(k)); +- Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(k); ++ this.chunksToUnload.remove(p_73158_1_, p_73158_2_); ++ Chunk chunk = (Chunk) this.loadedChunkHashMap.get(LongHash.toLong(p_73158_1_, p_73158_2_)); ++ boolean newChunk = false; // CraftBukkit + + if (chunk == null) + { +- boolean added = loadingChunks.add(k); ++ worldObj.timings.syncChunkLoadTimer.startTiming(); // Spigot ++ boolean added = loadingChunks.add(LongHash.toLong(p_73158_1_, p_73158_2_)); + if (!added) + { + cpw.mods.fml.common.FMLLog.bigWarning("There is an attempt to load a chunk (%d,%d) in dimension %d that is already being loaded. This will cause weird chunk breakages.", p_73158_1_, p_73158_2_, worldObj.provider.dimensionId); + } +- chunk = ForgeChunkManager.fetchDormantChunk(k, this.worldObj); ++ chunk = ForgeChunkManager.fetchDormantChunk(LongHash.toLong(p_73158_1_, p_73158_2_), this.worldObj); + if (chunk == null) + { + chunk = this.safeLoadChunk(p_73158_1_, p_73158_2_); +@@ -176,18 +215,39 @@ + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception generating new chunk"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("Chunk to be generated"); + crashreportcategory.addCrashSection("Location", String.format("%d,%d", new Object[] {Integer.valueOf(p_73158_1_), Integer.valueOf(p_73158_2_)})); +- crashreportcategory.addCrashSection("Position hash", Long.valueOf(k)); ++ crashreportcategory.addCrashSection("Position hash", LongHash.toLong(p_73158_1_, p_73158_2_)); + crashreportcategory.addCrashSection("Generator", this.currentChunkProvider.makeString()); + throw new ReportedException(crashreport); + } + } ++ ++ newChunk = true; // CraftBukkit + } + +- this.loadedChunkHashMap.add(k, chunk); +- this.loadedChunks.add(chunk); +- loadingChunks.remove(k); +- chunk.onChunkLoad(); ++ this.loadedChunkHashMap.put(LongHash.toLong(p_73158_1_, p_73158_2_), chunk); // CraftBukkit ++ this.loadedChunks.add(chunk); // Cauldron - vanilla compatibility ++ loadingChunks.remove(LongHash.toLong(p_73158_1_, p_73158_2_)); // Cauldron - LongHash ++ ++ if (chunk != null) ++ { ++ chunk.onChunkLoad(); ++ } ++ // CraftBukkit start ++ Server server = this.worldObj.getServer(); ++ ++ if (server != null) ++ { ++ /* ++ * If it's a new world, the first few chunks are generated inside ++ * the World constructor. We can't reliably alter that, so we have ++ * no way of creating a CraftWorld/CraftServer at that point. ++ */ ++ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(chunk.bukkitChunk, newChunk)); ++ } ++ ++ // CraftBukkit end + chunk.populateChunk(this, this, p_73158_1_, p_73158_2_); ++ worldObj.timings.syncChunkLoadTimer.stopTiming(); // Spigot + } + + return chunk; +@@ -195,11 +255,29 @@ + + public Chunk provideChunk(int p_73154_1_, int p_73154_2_) + { +- Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(p_73154_1_, p_73154_2_)); +- return chunk == null ? (!this.worldObj.findingSpawnPoint && !this.loadChunkOnProvideRequest ? this.defaultEmptyChunk : this.loadChunk(p_73154_1_, p_73154_2_)) : chunk; ++ // CraftBukkit start ++ Chunk chunk = (Chunk) this.loadedChunkHashMap.get(LongHash.toLong(p_73154_1_, p_73154_2_)); ++ chunk = chunk == null ? (shouldLoadChunk() ? this.loadChunk(p_73154_1_, p_73154_2_) : this.defaultEmptyChunk) : chunk; // Cauldron handle forge server tick events and load the chunk within 5 seconds of the world being loaded (for chunk loaders) ++ ++ if (chunk == this.defaultEmptyChunk) ++ { ++ return chunk; ++ } ++ ++ if (p_73154_1_ != chunk.xPosition || p_73154_2_ != chunk.zPosition) ++ { ++ logger.error("Chunk (" + chunk.xPosition + ", " + chunk.zPosition + ") stored at (" + p_73154_1_ + ", " + p_73154_2_ + ") in world '" + worldObj.getWorld().getName() + "'"); ++ logger.error(chunk.getClass().getName()); ++ Throwable ex = new Throwable(); ++ ex.fillInStackTrace(); ++ ex.printStackTrace(); ++ } ++ chunk.lastAccessedTick = MinecraftServer.getServer().getTickCounter(); // Cauldron ++ return chunk; ++ // CraftBukkit end + } + +- private Chunk safeLoadChunk(int p_73239_1_, int p_73239_2_) ++ public Chunk safeLoadChunk(int p_73239_1_, int p_73239_2_) // CraftBukkit - private -> public + { + if (this.currentChunkLoader == null) + { +@@ -209,6 +287,7 @@ + { + try + { ++ CauldronHooks.logChunkLoad(this, "Safe Load", p_73239_1_, p_73239_2_, false); // Cauldron + Chunk chunk = this.currentChunkLoader.loadChunk(this.worldObj, p_73239_1_, p_73239_2_); + + if (chunk != null) +@@ -217,8 +296,11 @@ + + if (this.currentChunkProvider != null) + { ++ worldObj.timings.syncChunkLoadStructuresTimer.startTiming(); // Spigot + this.currentChunkProvider.recreateStructures(p_73239_1_, p_73239_2_); ++ worldObj.timings.syncChunkLoadStructuresTimer.stopTiming(); // Spigot + } ++ chunk.lastAccessedTick = MinecraftServer.getServer().getTickCounter(); // Cauldron + } + + return chunk; +@@ -231,7 +313,7 @@ + } + } + +- private void safeSaveExtraChunkData(Chunk p_73243_1_) ++ public void safeSaveExtraChunkData(Chunk p_73243_1_) // CraftBukkit - private -> public + { + if (this.currentChunkLoader != null) + { +@@ -246,7 +328,7 @@ + } + } + +- private void safeSaveChunk(Chunk p_73242_1_) ++ public void safeSaveChunk(Chunk p_73242_1_) // CraftBukkit - private -> public + { + if (this.currentChunkLoader != null) + { +@@ -254,15 +336,18 @@ + { + p_73242_1_.lastSaveTime = this.worldObj.getTotalWorldTime(); + this.currentChunkLoader.saveChunk(this.worldObj, p_73242_1_); ++ // CraftBukkit start - IOException to Exception + } +- catch (IOException ioexception) ++ catch (Exception ioexception) + { + logger.error("Couldn\'t save chunk", ioexception); + } ++ /* Remove extra exception + catch (MinecraftException minecraftexception) + { + logger.error("Couldn\'t save chunk; already in use by another instance of Minecraft?", minecraftexception); + } ++ // CraftBukkit end */ + } + } + +@@ -277,6 +362,35 @@ + if (this.currentChunkProvider != null) + { + this.currentChunkProvider.populate(p_73153_1_, p_73153_2_, p_73153_3_); ++ // CraftBukkit start ++ BlockSand.fallInstantly = true; ++ Random random = new Random(); ++ random.setSeed(worldObj.getSeed()); ++ long xRand = random.nextLong() / 2L * 2L + 1L; ++ long zRand = random.nextLong() / 2L * 2L + 1L; ++ random.setSeed((long) p_73153_2_ * xRand + (long) p_73153_3_ * zRand ^ worldObj.getSeed()); ++ org.bukkit.World world = this.worldObj.getWorld(); ++ ++ if (world != null) ++ { ++ this.worldObj.populating = true; ++ ++ try ++ { ++ for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) ++ { ++ populator.populate(world, random, chunk.bukkitChunk); ++ } ++ } ++ finally ++ { ++ this.worldObj.populating = false; ++ } ++ } ++ ++ BlockSand.fallInstantly = false; ++ this.worldObj.getServer().getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(chunk.bukkitChunk)); ++ // CraftBukkit end + GameRegistry.generateWorld(p_73153_2_, p_73153_3_, worldObj, currentChunkProvider, p_73153_1_); + chunk.setChunkModified(); + } +@@ -286,11 +400,13 @@ + public boolean saveChunks(boolean p_73151_1_, IProgressUpdate p_73151_2_) + { + int i = 0; +- ArrayList arraylist = Lists.newArrayList(this.loadedChunks); ++ // Cauldron start - use thread-safe method for iterating loaded chunks ++ Object[] chunks = this.loadedChunks.toArray(); + +- for (int j = 0; j < arraylist.size(); ++j) ++ for (int j = 0; j < chunks.length; ++j) + { +- Chunk chunk = (Chunk)arraylist.get(j); ++ Chunk chunk = (Chunk)chunks[j]; ++ //Cauldron end + + if (p_73151_1_) + { +@@ -325,36 +441,60 @@ + { + if (!this.worldObj.levelSaving) + { +- for (ChunkCoordIntPair forced : this.worldObj.getPersistentChunks().keySet()) ++ // Cauldron start - remove any chunk that has a ticket associated with it ++ if (!this.chunksToUnload.isEmpty()) + { +- this.chunksToUnload.remove(ChunkCoordIntPair.chunkXZ2Int(forced.chunkXPos, forced.chunkZPos)); ++ for (ChunkCoordIntPair forcedChunk : this.worldObj.getPersistentChunks().keys()) ++ { ++ this.chunksToUnload.remove(forcedChunk.chunkXPos, forcedChunk.chunkZPos); ++ } + } ++ // Cauldron end ++ // CraftBukkit start ++ Server server = this.worldObj.getServer(); + +- for (int i = 0; i < 100; ++i) ++ for (int i = 0; i < 100 && !this.chunksToUnload.isEmpty(); i++) + { +- if (!this.chunksToUnload.isEmpty()) ++ long chunkcoordinates = this.chunksToUnload.popFirst(); ++ Chunk chunk = this.loadedChunkHashMap.get(chunkcoordinates); ++ ++ if (chunk == null) + { +- Long olong = (Long)this.chunksToUnload.iterator().next(); +- Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(olong.longValue()); ++ continue; ++ } + +- if (chunk != null) +- { +- chunk.onChunkUnload(); +- this.safeSaveChunk(chunk); +- this.safeSaveExtraChunkData(chunk); +- this.loadedChunks.remove(chunk); +- ForgeChunkManager.putDormantChunk(ChunkCoordIntPair.chunkXZ2Int(chunk.xPosition, chunk.zPosition), chunk); +- if(loadedChunks.size() == 0 && ForgeChunkManager.getPersistentChunksFor(this.worldObj).size() == 0 && !DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)){ +- DimensionManager.unloadWorld(this.worldObj.provider.dimensionId); +- return currentChunkProvider.unloadQueuedChunks(); +- } +- } ++ // Cauldron static - check if the chunk was accessed recently and keep it loaded if there are players in world ++ /*if (!shouldUnloadChunk(chunk) && this.worldObj.playerEntities.size() > 0) ++ { ++ CauldronHooks.logChunkUnload(this, chunk.xPosition, chunk.zPosition, "** Chunk kept from unloading due to recent activity"); ++ continue; ++ }*/ ++ // Cauldron end + +- this.chunksToUnload.remove(olong); +- this.loadedChunkHashMap.remove(olong.longValue()); ++ ++ ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk); ++ server.getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) ++ { ++ CauldronHooks.logChunkUnload(this, chunk.xPosition, chunk.zPosition, "Unloading Chunk at"); ++ ++ chunk.onChunkUnload(); ++ this.safeSaveChunk(chunk); ++ this.safeSaveExtraChunkData(chunk); ++ // this.unloadQueue.remove(olong); ++ this.loadedChunkHashMap.remove(chunkcoordinates); // CraftBukkit ++ this.loadedChunks.remove(chunk); // Cauldron - vanilla compatibility ++ ForgeChunkManager.putDormantChunk(chunkcoordinates, chunk); ++ if(this.loadedChunkHashMap.size() == 0 && ForgeChunkManager.getPersistentChunksFor(this.worldObj).size() == 0 && !DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)){ ++ DimensionManager.unloadWorld(this.worldObj.provider.dimensionId); ++ return currentChunkProvider.unloadQueuedChunks(); ++ } + } + } + ++ // CraftBukkit end ++ + if (this.currentChunkLoader != null) + { + this.currentChunkLoader.chunkTick(); +@@ -371,7 +511,8 @@ + + public String makeString() + { +- return "ServerChunkCache: " + this.loadedChunkHashMap.getNumHashElements() + " Drop: " + this.chunksToUnload.size(); ++ // CraftBukkit - this.chunks.count() -> .values().size() ++ return "ServerChunkCache: " + this.loadedChunkHashMap.values().size() + " Drop: " + this.chunksToUnload.size(); + } + + public List getPossibleCreatures(EnumCreatureType p_73155_1_, int p_73155_2_, int p_73155_3_, int p_73155_4_) +@@ -386,8 +527,32 @@ + + public int getLoadedChunkCount() + { +- return this.loadedChunkHashMap.getNumHashElements(); ++ // CraftBukkit - this.chunks.count() -> .values().size() ++ return this.loadedChunkHashMap.values().size(); + } + + public void recreateStructures(int p_82695_1_, int p_82695_2_) {} ++ ++ // Cauldron start ++ private boolean shouldLoadChunk() ++ { ++ return this.worldObj.findingSpawnPoint || ++ this.loadChunkOnProvideRequest || ++ (MinecraftServer.callingForgeTick && MinecraftServer.getServer().cauldronConfig.loadChunkOnForgeTick.getValue()) || ++ (MinecraftServer.currentTick - initialTick <= 100); ++ } ++ ++ public long lastAccessed(int x, int z) ++ { ++ long chunkHash = LongHash.toLong(x, z); ++ if (!loadedChunkHashMap.containsKey(chunkHash)) return 0; ++ return loadedChunkHashMap.get(chunkHash).lastAccessedTick; ++ } ++ ++ /*private boolean shouldUnloadChunk(Chunk chunk) ++ { ++ if (chunk == null) return false; ++ return MinecraftServer.getServer().getTickCounter() - chunk.lastAccessedTick > CauldronConfig.chunkGCGracePeriod.getValue(); ++ }*/ ++ // Cauldron end + } diff --git a/patches/net/minecraft/world/gen/feature/WorldGenShrub.java.patch b/patches/net/minecraft/world/gen/feature/WorldGenShrub.java.patch new file mode 100644 index 0000000..fda3ae8 --- /dev/null +++ b/patches/net/minecraft/world/gen/feature/WorldGenShrub.java.patch @@ -0,0 +1,16 @@ +--- ../src-base/minecraft/net/minecraft/world/gen/feature/WorldGenShrub.java ++++ ../src-work/minecraft/net/minecraft/world/gen/feature/WorldGenShrub.java +@@ -62,7 +62,13 @@ + } + } + } ++ // CraftBukkit start - Return false if gen was unsuccessful ++ } ++ else ++ { ++ return false; + } ++ // CraftBukkit end + + return true; + } diff --git a/patches/net/minecraft/world/gen/structure/MapGenStronghold.java.patch b/patches/net/minecraft/world/gen/structure/MapGenStronghold.java.patch new file mode 100644 index 0000000..57d8893 --- /dev/null +++ b/patches/net/minecraft/world/gen/structure/MapGenStronghold.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/world/gen/structure/MapGenStronghold.java ++++ ../src-work/minecraft/net/minecraft/world/gen/structure/MapGenStronghold.java +@@ -92,7 +92,17 @@ + double d1 = (1.25D * (double)l + random.nextDouble()) * this.field_82671_h * (double)l; + int j1 = (int)Math.round(Math.cos(d0) * d1); + int k1 = (int)Math.round(Math.sin(d0) * d1); +- ChunkPosition chunkposition = this.worldObj.getWorldChunkManager().findBiomePosition((j1 << 4) + 8, (k1 << 4) + 8, 112, this.field_151546_e, random); ++ // Cauldron start - catch invalid positions ++ ChunkPosition chunkposition = null; ++ try ++ { ++ chunkposition = this.worldObj.getWorldChunkManager().findBiomePosition((j1 << 4) + 8, (k1 << 4) + 8, 112, this.field_151546_e, random); ++ } ++ catch (ArrayIndexOutOfBoundsException e) ++ { ++ // ignore ++ } ++ // Cauldron end + + if (chunkposition != null) + { diff --git a/patches/net/minecraft/world/gen/structure/MapGenStructure.java.patch b/patches/net/minecraft/world/gen/structure/MapGenStructure.java.patch new file mode 100644 index 0000000..1fc1fa0 --- /dev/null +++ b/patches/net/minecraft/world/gen/structure/MapGenStructure.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/net/minecraft/world/gen/structure/MapGenStructure.java ++++ ../src-work/minecraft/net/minecraft/world/gen/structure/MapGenStructure.java +@@ -239,8 +239,17 @@ + { + if (this.field_143029_e == null) + { +- this.field_143029_e = (MapGenStructureData)p_143027_1_.perWorldStorage.loadData(MapGenStructureData.class, this.func_143025_a()); ++ // Spigot Start ++ if (p_143027_1_.getSpigotConfig().saveStructureInfo && !this.func_143025_a().equals("Mineshaft")) // Cauldron ++ { ++ this.field_143029_e = (MapGenStructureData) p_143027_1_.loadItemData(MapGenStructureData.class, this.func_143025_a()); ++ } ++ else ++ { ++ this.field_143029_e = new MapGenStructureData(this.func_143025_a()); ++ } + ++ // Spigot End + if (this.field_143029_e == null) + { + this.field_143029_e = new MapGenStructureData(this.func_143025_a()); diff --git a/patches/net/minecraft/world/gen/structure/StructureStart.java.patch b/patches/net/minecraft/world/gen/structure/StructureStart.java.patch new file mode 100644 index 0000000..80d2af6 --- /dev/null +++ b/patches/net/minecraft/world/gen/structure/StructureStart.java.patch @@ -0,0 +1,12 @@ +--- ../src-base/minecraft/net/minecraft/world/gen/structure/StructureStart.java ++++ ../src-work/minecraft/net/minecraft/world/gen/structure/StructureStart.java +@@ -41,7 +41,8 @@ + { + StructureComponent structurecomponent = (StructureComponent)iterator.next(); + +- if (structurecomponent.getBoundingBox().intersectsWith(p_75068_3_) && !structurecomponent.addComponentParts(p_75068_1_, p_75068_2_, p_75068_3_)) ++ // Cauldron - validate structurecomponent ++ if ((structurecomponent == null || structurecomponent.getBoundingBox() == null) || (structurecomponent.getBoundingBox().intersectsWith(p_75068_3_) && !structurecomponent.addComponentParts(p_75068_1_, p_75068_2_, p_75068_3_))) + { + iterator.remove(); + } diff --git a/patches/net/minecraft/world/storage/ISaveHandler.java.patch b/patches/net/minecraft/world/storage/ISaveHandler.java.patch new file mode 100644 index 0000000..c0d8b88 --- /dev/null +++ b/patches/net/minecraft/world/storage/ISaveHandler.java.patch @@ -0,0 +1,9 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/ISaveHandler.java ++++ ../src-work/minecraft/net/minecraft/world/storage/ISaveHandler.java +@@ -27,4 +27,6 @@ + File getMapFileFromName(String p_75758_1_); + + String getWorldDirectoryName(); ++ ++ java.util.UUID getUUID(); // CraftBukkit + } diff --git a/patches/net/minecraft/world/storage/MapData.java.patch b/patches/net/minecraft/world/storage/MapData.java.patch new file mode 100644 index 0000000..fb5fc73 --- /dev/null +++ b/patches/net/minecraft/world/storage/MapData.java.patch @@ -0,0 +1,116 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/MapData.java ++++ ../src-work/minecraft/net/minecraft/world/storage/MapData.java +@@ -14,6 +14,14 @@ + import net.minecraft.world.World; + import net.minecraft.world.WorldSavedData; + ++// CraftBukkit start ++import java.util.UUID; ++ ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.map.CraftMapView; ++// CraftBukkit end ++ + public class MapData extends WorldSavedData + { + public int xCenter; +@@ -24,11 +32,21 @@ + public List playersArrayList = new ArrayList(); + private Map playersHashMap = new HashMap(); + public Map playersVisibleOnMap = new LinkedHashMap(); ++ ++ // CraftBukkit start ++ public final CraftMapView mapView; ++ private CraftServer server; ++ private UUID uniqueId = null; ++ // CraftBukkit end + private static final String __OBFID = "CL_00000577"; + + public MapData(String p_i2140_1_) + { + super(p_i2140_1_); ++ // CraftBukkit start ++ mapView = new CraftMapView(this); ++ server = (CraftServer) org.bukkit.Bukkit.getServer(); ++ // CraftBukkit end + } + + public void readFromNBT(NBTTagCompound p_76184_1_) +@@ -107,7 +125,7 @@ + { + if (!this.playersHashMap.containsKey(p_76191_1_)) + { +- MapData.MapInfo mapinfo = new MapData.MapInfo(p_76191_1_); ++ MapData.MapInfo mapinfo = new MapData.MapInfo(this, p_76191_1_); // Cauldron + this.playersHashMap.put(p_76191_1_, mapinfo); + this.playersArrayList.add(mapinfo); + } +@@ -265,7 +283,7 @@ + + if (mapinfo == null) + { +- mapinfo = new MapData.MapInfo(p_82568_1_); ++ mapinfo = new MapData.MapInfo(this, p_82568_1_); // Cauldron + this.playersHashMap.put(p_82568_1_, mapinfo); + this.playersArrayList.add(mapinfo); + } +@@ -300,10 +318,12 @@ + private byte[] lastPlayerLocationOnMap; + public int field_82569_d; + private boolean field_82570_i; ++ final MapData mapDataObj; // Cauldron + private static final String __OBFID = "CL_00000578"; + +- public MapInfo(EntityPlayer p_i2138_2_) ++ public MapInfo(MapData mapData, EntityPlayer p_i2138_2_) + { ++ this.mapDataObj = mapData; // Cauldron + this.entityplayerObj = p_i2138_2_; + + for (int i = 0; i < this.field_76209_b.length; ++i) +@@ -328,20 +348,37 @@ + int i; + int i1; + ++ // Spigot start ++ boolean custom = this.mapDataObj.mapView.renderers.size() > 1 || !(this.mapDataObj.mapView.renderers.get(0) instanceof org.bukkit.craftbukkit.map.CraftMapRenderer); ++ org.bukkit.craftbukkit.map.RenderData render = (custom) ? this.mapDataObj.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) entityplayerObj.getBukkitEntity()) : null; // CraftBukkit ++ + if (--this.ticksUntilPlayerLocationMapUpdate < 0) + { + this.ticksUntilPlayerLocationMapUpdate = 4; +- abyte = new byte[MapData.this.playersVisibleOnMap.size() * 3 + 1]; ++ abyte = new byte[((custom) ? render.cursors.size() : this.mapDataObj.playersVisibleOnMap.size()) * 3 + 1]; // CraftBukkit + abyte[0] = 1; + i = 0; +- +- for (Iterator iterator = MapData.this.playersVisibleOnMap.values().iterator(); iterator.hasNext(); ++i) ++ ++ // CraftBukkit start ++ ++ // Spigot start ++ for (Iterator iterator = ((custom) ? render.cursors.iterator() : this.mapDataObj.playersVisibleOnMap.values().iterator()); iterator.hasNext(); ++i) + { +- MapData.MapCoord mapcoord = (MapData.MapCoord)iterator.next(); +- abyte[i * 3 + 1] = (byte)(mapcoord.iconSize << 4 | mapcoord.iconRotation & 15); +- abyte[i * 3 + 2] = mapcoord.centerX; +- abyte[i * 3 + 3] = mapcoord.centerZ; ++ org.bukkit.map.MapCursor cursor = (custom) ? (org.bukkit.map.MapCursor) iterator.next() : null; ++ ++ if (cursor != null && !cursor.isVisible()) ++ { ++ continue; ++ } ++ ++ MapCoord deco = (custom) ? null : (MapCoord) iterator.next(); ++ abyte[i * 3 + 1] = (byte)(((custom) ? cursor.getRawType() : deco.iconSize) << 4 | ((custom) ? cursor.getDirection() : deco.iconRotation) & 15); ++ abyte[i * 3 + 2] = (byte)((custom) ? cursor.getX() : deco.centerX); ++ abyte[i * 3 + 3] = (byte)((custom) ? cursor.getY() : deco.centerZ); + } ++ ++ // Spigot end ++ // CraftBukkit end + + boolean flag = !p_76204_1_.isOnItemFrame(); + diff --git a/patches/net/minecraft/world/storage/SaveHandler.java.patch b/patches/net/minecraft/world/storage/SaveHandler.java.patch new file mode 100644 index 0000000..51ec1a6 --- /dev/null +++ b/patches/net/minecraft/world/storage/SaveHandler.java.patch @@ -0,0 +1,233 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/SaveHandler.java ++++ ../src-work/minecraft/net/minecraft/world/storage/SaveHandler.java +@@ -10,6 +10,7 @@ + import cpw.mods.fml.common.FMLCommonHandler; + import cpw.mods.fml.common.StartupQuery; + import net.minecraft.entity.player.EntityPlayer; ++import net.minecraft.entity.player.EntityPlayerMP; + import net.minecraft.nbt.CompressedStreamTools; + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.server.MinecraftServer; +@@ -19,6 +20,13 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.util.UUID; ++ ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++// CraftBukkit end ++import cpw.mods.fml.common.registry.GameData; // Cauldron ++ + public class SaveHandler implements ISaveHandler, IPlayerFileData + { + private static final Logger logger = LogManager.getLogger(); +@@ -27,6 +35,8 @@ + private final File mapDataDir; + private final long initializationTime = MinecraftServer.getSystemTimeMillis(); + private final String saveDirectoryName; ++ private UUID uuid = null; // CraftBukkit ++ private static boolean initializedBukkit = false; // Cauldron + private static final String __OBFID = "CL_00000585"; + + public SaveHandler(File p_i2146_1_, String p_i2146_2_, boolean p_i2146_3_) +@@ -65,7 +75,7 @@ + catch (IOException ioexception) + { + ioexception.printStackTrace(); +- throw new RuntimeException("Failed to check session lock, aborting"); ++ throw new RuntimeException("Failed to check session lock for world " + this.worldDirectory + ", aborting"); // Cauldron + } + } + +@@ -85,7 +95,7 @@ + { + if (datainputstream.readLong() != this.initializationTime) + { +- throw new MinecraftException("The save is being accessed from another location, aborting"); ++ throw new MinecraftException("The save folder for world " + this.worldDirectory + " is being accessed from another location, aborting"); // Cauldron + } + } + finally +@@ -95,7 +105,10 @@ + } + catch (IOException ioexception) + { +- throw new MinecraftException("Failed to check session lock, aborting"); ++ // Cauldron start ++ ioexception.printStackTrace(); ++ throw new MinecraftException("Failed to check session lock for world " + this.worldDirectory + ", aborting"); ++ // Cauldron end + } + } + +@@ -120,6 +133,7 @@ + nbttagcompound1 = nbttagcompound.getCompoundTag("Data"); + worldInfo = new WorldInfo(nbttagcompound1); + FMLCommonHandler.instance().handleWorldDataLoad(this, worldInfo, nbttagcompound); ++ this.initBukkitData(worldInfo); // Cauldron + return worldInfo; + } + catch (StartupQuery.AbortedException e) +@@ -143,6 +157,7 @@ + nbttagcompound1 = nbttagcompound.getCompoundTag("Data"); + worldInfo = new WorldInfo(nbttagcompound1); + FMLCommonHandler.instance().handleWorldDataLoad(this, worldInfo, nbttagcompound); ++ this.initBukkitData(worldInfo); // Cauldron + return worldInfo; + } + catch (StartupQuery.AbortedException e) +@@ -154,7 +169,7 @@ + exception.printStackTrace(); + } + } +- ++ this.initBukkitData(worldInfo); // Cauldron + return null; + } + +@@ -282,6 +297,18 @@ + + if (nbttagcompound != null) + { ++ // CraftBukkit start ++ if (p_75752_1_ instanceof EntityPlayerMP) ++ { ++ CraftPlayer player = (CraftPlayer) p_75752_1_.getBukkitEntity(); // Cauldron ++ // Only update first played if it is older than the one we have ++ long modified = new File(playersDirectory, p_75752_1_.getCommandSenderName() + ".dat").lastModified(); ++ if (modified < player.getFirstPlayed()) { ++ player.setFirstPlayed(modified); ++ } ++ } ++ // CraftBukkit end ++ + p_75752_1_.readFromNBT(nbttagcompound); + } + +@@ -289,6 +316,27 @@ + return nbttagcompound; + } + ++ // CraftBukkit start ++ public NBTTagCompound getPlayerData(String par1Str) ++ { ++ try ++ { ++ File file1 = new File(this.playersDirectory, par1Str + ".dat"); ++ ++ if (file1.exists()) ++ { ++ return CompressedStreamTools.readCompressed(new FileInputStream(file1)); ++ } ++ } ++ catch (Exception exception) ++ { ++ logger.warn("Failed to load player data for " + par1Str); ++ } ++ ++ return null; ++ } ++ // CraftBukkit end ++ + public IPlayerFileData getSaveHandler() + { + return this; +@@ -320,4 +368,97 @@ + { + return this.saveDirectoryName; + } ++ ++ // CraftBukkit start ++ public UUID getUUID() ++ { ++ if (uuid != null) ++ { ++ return uuid; ++ } ++ ++ File file1 = new File(this.worldDirectory, "uid.dat"); ++ ++ if (file1.exists()) ++ { ++ DataInputStream dis = null; ++ ++ try ++ { ++ dis = new DataInputStream(new FileInputStream(file1)); ++ return uuid = new UUID(dis.readLong(), dis.readLong()); ++ } ++ catch (IOException ex) ++ { ++ logger.warn("Failed to read " + file1 + ", generating new random UUID", ex); ++ } ++ finally ++ { ++ if (dis != null) ++ { ++ try ++ { ++ dis.close(); ++ } ++ catch (IOException ex) ++ { ++ // NOOP ++ } ++ } ++ } ++ } ++ ++ uuid = UUID.randomUUID(); ++ DataOutputStream dos = null; ++ ++ try ++ { ++ dos = new DataOutputStream(new FileOutputStream(file1)); ++ dos.writeLong(uuid.getMostSignificantBits()); ++ dos.writeLong(uuid.getLeastSignificantBits()); ++ } ++ catch (IOException ex) ++ { ++ logger.warn("Failed to write " + file1, ex); ++ } ++ finally ++ { ++ if (dos != null) ++ { ++ try ++ { ++ dos.close(); ++ } ++ catch (IOException ex) ++ { ++ // NOOP ++ } ++ } ++ } ++ ++ return uuid; ++ } ++ ++ public File getPlayerDir() ++ { ++ return playersDirectory; ++ } ++ // CraftBukkit end ++ ++ // Cauldron start ++ public void initBukkitData(WorldInfo worldInfo) ++ { ++ // inject bukkit materials before plugins load ++ if (!this.initializedBukkit && (worldInfo == null || worldInfo.getDimension() == 0)) ++ { ++ GameData.injectBlockBukkitMaterials(); ++ GameData.injectItemBukkitMaterials(); ++ // since we modify bukkit enums, we need to guarantee that plugins are ++ // loaded after all mods have been loaded by FML to avoid race conditions. ++ MinecraftServer.getServer().server.loadPlugins(); ++ MinecraftServer.getServer().server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.STARTUP); ++ this.initializedBukkit = true; ++ } ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/world/storage/SaveHandlerMP.java.patch b/patches/net/minecraft/world/storage/SaveHandlerMP.java.patch new file mode 100644 index 0000000..65e5baa --- /dev/null +++ b/patches/net/minecraft/world/storage/SaveHandlerMP.java.patch @@ -0,0 +1,24 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/SaveHandlerMP.java ++++ ../src-work/minecraft/net/minecraft/world/storage/SaveHandlerMP.java +@@ -3,6 +3,8 @@ + import cpw.mods.fml.relauncher.Side; + import cpw.mods.fml.relauncher.SideOnly; + import java.io.File; ++import java.util.UUID; ++ + import net.minecraft.nbt.NBTTagCompound; + import net.minecraft.world.MinecraftException; + import net.minecraft.world.WorldProvider; +@@ -50,4 +52,12 @@ + { + return null; + } ++ ++ // Cauldron start ++ @Override ++ public UUID getUUID() ++ { ++ return this.getUUID(); ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraft/world/storage/WorldInfo.java.patch b/patches/net/minecraft/world/storage/WorldInfo.java.patch new file mode 100644 index 0000000..1db2bc9 --- /dev/null +++ b/patches/net/minecraft/world/storage/WorldInfo.java.patch @@ -0,0 +1,48 @@ +--- ../src-base/minecraft/net/minecraft/world/storage/WorldInfo.java ++++ ../src-work/minecraft/net/minecraft/world/storage/WorldInfo.java +@@ -117,6 +117,7 @@ + this.thunderTime = p_i2157_1_.getInteger("thunderTime"); + this.thundering = p_i2157_1_.getBoolean("thundering"); + this.hardcore = p_i2157_1_.getBoolean("hardcore"); ++ this.dimension = p_i2157_1_.getInteger("dimension"); // Cauldron + + if (p_i2157_1_.hasKey("initialized", 99)) + { +@@ -193,6 +194,7 @@ + this.allowCommands = p_i2159_1_.allowCommands; + this.initialized = p_i2159_1_.initialized; + this.theGameRules = p_i2159_1_.theGameRules; ++ this.dimension = p_i2159_1_.dimension; // Cauldron + } + + public NBTTagCompound getNBTTagCompound() +@@ -234,6 +236,7 @@ + p_76064_1_.setBoolean("allowCommands", this.allowCommands); + p_76064_1_.setBoolean("initialized", this.initialized); + p_76064_1_.setTag("GameRules", this.theGameRules.writeGameRulesToNBT()); ++ p_76064_1_.setInteger("dimension", this.dimension); // Cauldron + + if (p_76064_2_ != null) + { +@@ -282,6 +285,21 @@ + return this.playerTag; + } + ++ // Cauldron start ++ /** ++ * Sets the Dimension. ++ */ ++ public void setDimension(int dim) ++ { ++ this.dimension = dim; ++ } ++ ++ public int getDimension() ++ { ++ return this.dimension; ++ } ++ // Cauldron end ++ + public int getVanillaDimension() + { + return this.dimension; diff --git a/patches/net/minecraftforge/common/DimensionManager.java.patch b/patches/net/minecraftforge/common/DimensionManager.java.patch new file mode 100644 index 0000000..d1b9601 --- /dev/null +++ b/patches/net/minecraftforge/common/DimensionManager.java.patch @@ -0,0 +1,376 @@ +--- ../src-base/minecraft/net/minecraftforge/common/DimensionManager.java ++++ ../src-work/minecraft/net/minecraftforge/common/DimensionManager.java +@@ -34,6 +34,15 @@ + import net.minecraft.world.storage.ISaveHandler; + import net.minecraft.world.storage.SaveHandler; + import net.minecraftforge.event.world.WorldEvent; ++// Cauldron start ++import net.minecraft.server.dedicated.DedicatedServer; ++import net.minecraft.world.chunk.storage.AnvilSaveHandler; ++import net.minecraftforge.cauldron.CauldronUtils; ++import net.minecraftforge.common.util.EnumHelper; ++import org.bukkit.World.Environment; ++import org.bukkit.WorldCreator; ++import org.bukkit.generator.ChunkGenerator; ++// Cauldron end + + public class DimensionManager + { +@@ -46,6 +55,11 @@ + private static BitSet dimensionMap = new BitSet(Long.SIZE << 4); + private static ConcurrentMap weakWorldMap = new MapMaker().weakKeys().weakValues().makeMap(); + private static Multiset leakedWorlds = HashMultiset.create(); ++ // Cauldron start ++ private static Hashtable, Integer> classToProviders = new Hashtable, Integer>(); ++ private static ArrayList bukkitDims = new ArrayList(); // used to keep track of Bukkit dimensions ++ private static final String FILE_SEPARATOR = System.getProperty("file.separator"); ++ // Cauldron end + + public static boolean registerProviderType(int id, Class provider, boolean keepLoaded) + { +@@ -53,7 +67,23 @@ + { + return false; + } ++ // Cauldron start - register provider with bukkit and add appropriate config option ++ String worldType = "unknown"; ++ if (id != -1 && id != 0 && id != 1) // ignore vanilla ++ { ++ worldType = provider.getSimpleName().toLowerCase(); ++ worldType = worldType.replace("worldprovider", ""); ++ worldType = worldType.replace("provider", ""); ++ registerBukkitEnvironment(id, worldType); ++ } ++ else ++ { ++ worldType = Environment.getEnvironment(id).name().toLowerCase(); ++ } ++ keepLoaded = MinecraftServer.getServer().cauldronConfig.getBoolean("world-environment-settings." + worldType + ".keep-world-loaded", keepLoaded); ++ // Cauldron end + providers.put(id, provider); ++ classToProviders.put(provider, id); + spawnSettings.put(id, keepLoaded); + return true; + } +@@ -157,28 +187,33 @@ + + public static Integer[] getIDs(boolean check) + { +- if (check) ++ // Cauldron start - check config option and only log world leak messages if enabled ++ if (MinecraftServer.getServer().cauldronConfig.worldLeakDebug.getValue()) + { +- List allWorlds = Lists.newArrayList(weakWorldMap.keySet()); +- allWorlds.removeAll(worlds.values()); +- for (ListIterator li = allWorlds.listIterator(); li.hasNext(); ) ++ if (check) + { +- World w = li.next(); +- leakedWorlds.add(System.identityHashCode(w)); +- } +- for (World w : allWorlds) +- { +- int leakCount = leakedWorlds.count(System.identityHashCode(w)); +- if (leakCount == 5) ++ List allWorlds = Lists.newArrayList(weakWorldMap.keySet()); ++ allWorlds.removeAll(worlds.values()); ++ for (ListIterator li = allWorlds.listIterator(); li.hasNext(); ) + { +- FMLLog.fine("The world %x (%s) may have leaked: first encounter (5 occurences).\n", System.identityHashCode(w), w.getWorldInfo().getWorldName()); ++ World w = li.next(); ++ leakedWorlds.add(System.identityHashCode(w)); + } +- else if (leakCount % 5 == 0) ++ for (World w : allWorlds) + { +- FMLLog.fine("The world %x (%s) may have leaked: seen %d times.\n", System.identityHashCode(w), w.getWorldInfo().getWorldName(), leakCount); ++ int leakCount = leakedWorlds.count(System.identityHashCode(w)); ++ if (leakCount == 5) ++ { ++ FMLLog.fine("The world %x (%s) may have leaked: first encounter (5 occurences). Note: This may be a caused by a mod, plugin, or just a false-positive(No memory leak). If server crashes due to OOM, report to Cauldron.\n", System.identityHashCode(w), w.getWorldInfo().getWorldName()); ++ } ++ else if (leakCount % 5 == 0) ++ { ++ FMLLog.fine("The world %x (%s) may have leaked: seen %d times. Note: This may be a caused by a mod, plugin, or just a false-positive(No memory leak). If server crashes due to OOM, report to Cauldron.\n", System.identityHashCode(w), w.getWorldInfo().getWorldName(), leakCount); ++ } + } + } + } ++ // Cauldron end + return getIDs(); + } + public static Integer[] getIDs() +@@ -191,12 +226,23 @@ + if (world != null) + { + worlds.put(id, world); +- weakWorldMap.put(world, world); ++ // Cauldron start - check config option and only log world leak messages if enabled ++ if (MinecraftServer.getServer().cauldronConfig.worldLeakDebug.getValue()) ++ { ++ weakWorldMap.put(world, world); ++ } ++ // handle all world adds here for Bukkit ++ if (!MinecraftServer.getServer().worlds.contains(world)) ++ { ++ MinecraftServer.getServer().worlds.add(world); ++ } ++ // Cauldron end + MinecraftServer.getServer().worldTickTimes.put(id, new long[100]); + FMLLog.info("Loading dimension %d (%s) (%s)", id, world.getWorldInfo().getWorldName(), world.func_73046_m()); + } + else + { ++ MinecraftServer.getServer().worlds.remove(getWorld(id)); // Cauldron - remove world from our new world arraylist + worlds.remove(id); + MinecraftServer.getServer().worldTickTimes.remove(id); + FMLLog.info("Unloading dimension %d", id); +@@ -224,6 +270,7 @@ + } + + public static void initDimension(int dim) { ++ if (dim == 0) return; // Cauldron - overworld + WorldServer overworld = getWorld(0); + if (overworld == null) + { +@@ -231,6 +278,12 @@ + } + try + { ++ // Cauldron start - Fixes MultiVerse issue when mods such as Twilight Forest try to hotload their dimension when using its WorldProvider ++ if(net.minecraftforge.cauldron.CauldronHooks.craftWorldLoading) ++ { ++ return; ++ } ++ // Cauldron end + DimensionManager.getProviderType(dim); + } + catch (Exception e) +@@ -242,9 +295,52 @@ + ISaveHandler savehandler = overworld.getSaveHandler(); + WorldSettings worldSettings = new WorldSettings(overworld.getWorldInfo()); + +- WorldServer world = (dim == 0 ? overworld : new WorldServerMulti(mcServer, savehandler, overworld.getWorldInfo().getWorldName(), dim, worldSettings, overworld, mcServer.theProfiler)); ++ // Cauldron start - handles hotloading dimensions ++ String worldType; ++ String name; ++ String oldName = ""; ++ Environment env = Environment.getEnvironment(getProviderType(dim)); ++ if (dim >= -1 && dim <= 1) ++ { ++ if ((dim == -1 && !mcServer.getAllowNether()) || (dim == 1 && !mcServer.server.getAllowEnd())) ++ return; ++ worldType = env.toString().toLowerCase(); ++ name = "DIM" + dim; ++ } ++ else ++ { ++ WorldProvider provider = WorldProvider.getProviderForDimension(dim); ++ worldType = provider.getClass().getSimpleName().toLowerCase(); ++ worldType = worldType.replace("worldprovider", ""); ++ oldName = "world_" + worldType; ++ worldType = worldType.replace("provider", ""); ++ ++ if (Environment.getEnvironment(DimensionManager.getProviderType(dim)) == null) ++ env = DimensionManager.registerBukkitEnvironment(DimensionManager.getProviderType(provider.getClass()), worldType); ++ ++ name = provider.getSaveFolder(); ++ if (name == null) name = "DIM0"; ++ } ++ // add ability to disable dimensions ++ if (!MinecraftServer.getServer().cauldronConfig.getBoolean("world-environment-settings." + worldType + ".enabled", true)) ++ return; ++ ++ CauldronUtils.migrateWorlds(worldType, oldName, overworld.getWorldInfo().getWorldName(), name); // Cauldron ++ ChunkGenerator gen = mcServer.server.getGenerator(name); ++ if (mcServer instanceof DedicatedServer) { ++ worldSettings.func_82750_a(((DedicatedServer) mcServer).getStringProperty("generator-settings", "")); ++ } ++ WorldServer world = new WorldServerMulti(mcServer, new AnvilSaveHandler(mcServer.server.getWorldContainer(), name, true), name, dim, worldSettings, overworld, mcServer.theProfiler, env, gen); ++ ++ if (gen != null) ++ { ++ world.getWorld().getPopulators().addAll(gen.getDefaultPopulators(world.getWorld())); ++ } ++ mcServer.getConfigurationManager().setPlayerManager(mcServer.worlds.toArray(new WorldServer[mcServer.worlds.size()])); + world.addWorldAccess(new WorldManager(mcServer, world)); + MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world)); ++ mcServer.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(world.getWorld())); ++ // Cauldron end + if (!mcServer.isSinglePlayer()) + { + world.getWorldInfo().setGameType(mcServer.getGameType()); +@@ -253,6 +349,79 @@ + mcServer.func_147139_a(mcServer.func_147135_j()); + } + ++ // Cauldron start - new method for handling creation of Bukkit dimensions. Currently supports MultiVerse ++ public static WorldServer initDimension(WorldCreator creator, WorldSettings worldSettings) { ++ WorldServer overworld = getWorld(0); ++ if (overworld == null) { ++ throw new RuntimeException("Cannot Hotload Dim: Overworld is not Loaded!"); ++ } ++ ++ MinecraftServer mcServer = overworld.func_73046_m(); ++ ++ String worldType; ++ String name; ++ ++ int providerId = 0; ++ if (creator.environment() != null) ++ providerId = creator.environment().getId(); ++ try { ++ providerId = getProviderType(providerId); ++ } ++ catch (IllegalArgumentException e) ++ { ++ // do nothing ++ } ++ ++ Environment env = creator.environment(); ++ worldType = env.name().toLowerCase(); ++ name = creator.name(); ++ int dim = 0; ++ // Use saved dimension from level.dat if it exists. This guarantees that after a world is created, the same dimension will be used. Fixes issues with MultiVerse ++ AnvilSaveHandler saveHandler = new AnvilSaveHandler(mcServer.server.getWorldContainer(), name, true); ++ if (saveHandler.loadWorldInfo() != null) ++ { ++ int savedDim = saveHandler.loadWorldInfo().getDimension(); ++ if (savedDim != 0 && savedDim != -1 && savedDim != 1) ++ { ++ dim = savedDim; ++ } ++ } ++ if (dim == 0) ++ { ++ dim = getNextFreeDimId(); ++ } ++ ++ if (!isDimensionRegistered(dim)) // handle reloads properly ++ { ++ registerDimension(dim, providerId); ++ addBukkitDimension(dim); ++ } ++ ChunkGenerator gen = creator.generator(); ++ if (mcServer instanceof DedicatedServer) { ++ worldSettings.func_82750_a(((DedicatedServer) mcServer).getStringProperty("generator-settings", "")); ++ } ++ ++ WorldServer world = new WorldServerMulti(mcServer, saveHandler, name, dim, worldSettings, overworld, mcServer.theProfiler, env, gen); ++ ++ if (gen != null) ++ { ++ world.getWorld().getPopulators().addAll(gen.getDefaultPopulators(world.getWorld())); ++ } ++ world.provider.dimensionId = dim; // Fix for TerrainControl injecting their own WorldProvider ++ mcServer.getConfigurationManager().setPlayerManager(mcServer.worlds.toArray(new WorldServer[mcServer.worlds.size()])); ++ ++ world.addWorldAccess(new WorldManager(mcServer, world)); ++ MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world)); ++ if (!mcServer.isSinglePlayer()) ++ { ++ world.getWorldInfo().setGameType(mcServer.getGameType()); ++ } ++ mcServer.func_147139_a(mcServer.func_147135_j()); ++ ++ return world; ++ } ++ // Cauldron end ++ + public static WorldServer getWorld(int id) + { + return worlds.get(id); +@@ -266,7 +435,7 @@ + public static boolean shouldLoadSpawn(int dim) + { + int id = getProviderType(dim); +- return spawnSettings.containsKey(id) && spawnSettings.get(id); ++ return ((spawnSettings.containsKey(id) && spawnSettings.get(id)) || (getWorld(dim) != null && getWorld(dim).keepSpawnInMemory)); // Cauldron added bukkit check + } + + static +@@ -306,7 +475,8 @@ + } + + public static void unloadWorld(int id) { +- unloadQueue.add(id); ++ if (!shouldLoadSpawn(id)) // Cauldron - prevent mods from force unloading if we have it disabled ++ unloadQueue.add(id); + } + + /* +@@ -315,26 +485,9 @@ + public static void unloadWorlds(Hashtable worldTickTimes) { + for (int id : unloadQueue) { + WorldServer w = worlds.get(id); +- try { +- if (w != null) +- { +- w.saveAllChunks(true, null); +- } +- else +- { +- FMLLog.warning("Unexpected world unload - world %d is already unloaded", id); +- } +- } catch (MinecraftException e) { +- e.printStackTrace(); +- } +- finally ++ if (w != null) + { +- if (w != null) +- { +- MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(w)); +- w.flush(); +- setWorld(id, null); +- } ++ MinecraftServer.getServer().server.unloadWorld(w.getWorld(), true); // Cauldron - unload through our new method for simplicity + } + } + unloadQueue.clear(); +@@ -425,4 +578,45 @@ + return null; + } + } ++ ++ // Cauldron start - add registration for Bukkit Environments ++ public static Environment registerBukkitEnvironment(int dim, String providerName) ++ { ++ Environment env = Environment.getEnvironment(dim); ++ if (env == null) // Cauldron if environment not found, register one ++ { ++ providerName = providerName.replace("WorldProvider", ""); ++ env = EnumHelper.addBukkitEnvironment(dim, providerName.toUpperCase()); ++ Environment.registerEnvironment(env); ++ } ++ return env; ++ } ++ ++ public static int getProviderType(Class provider) ++ { ++ return classToProviders.get(provider); ++ } ++ ++ public static void addBukkitDimension(int dim) ++ { ++ if (!bukkitDims.contains(dim)) ++ bukkitDims.add(dim); ++ } ++ ++ public static void removeBukkitDimension(int dim) ++ { ++ if (bukkitDims.contains(dim)) ++ bukkitDims.remove(bukkitDims.indexOf(dim)); ++ } ++ ++ public static ArrayList getBukkitDimensionIDs() ++ { ++ return bukkitDims; ++ } ++ ++ public static boolean isBukkitDimension(int dim) ++ { ++ return bukkitDims.contains(dim); ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraftforge/common/ForgeHooks.java.patch b/patches/net/minecraftforge/common/ForgeHooks.java.patch new file mode 100644 index 0000000..c63caa8 --- /dev/null +++ b/patches/net/minecraftforge/common/ForgeHooks.java.patch @@ -0,0 +1,45 @@ +--- ../src-base/minecraft/net/minecraftforge/common/ForgeHooks.java ++++ ../src-work/minecraft/net/minecraftforge/common/ForgeHooks.java +@@ -63,6 +63,7 @@ + import net.minecraftforge.event.world.BlockEvent; + import net.minecraftforge.event.world.NoteBlockEvent; + import static net.minecraft.init.Blocks.*; ++import net.minecraftforge.common.util.FakePlayer; // Cauldron + + public class ForgeHooks + { +@@ -439,6 +440,8 @@ + + public static BlockEvent.BreakEvent onBlockBreakEvent(World world, GameType gameType, EntityPlayerMP entityPlayer, int x, int y, int z) + { ++ // Cauldron - pre-cancel handled in BreakEvent ++ /* + // Logic from tryHarvestBlock for pre-canceling the event + boolean preCancelEvent = false; + if (gameType.isAdventure() && !entityPlayer.isCurrentToolAdventureModeExempt(x, y, z)) +@@ -449,9 +452,9 @@ + { + preCancelEvent = true; + } +- ++ */ + // Tell client the block is gone immediately then process events +- if (world.getTileEntity(x, y, z) == null) ++ if (world.getTileEntity(x, y, z) == null && !(entityPlayer instanceof FakePlayer)) // Cauldron - don't send packets to fakeplayers + { + S23PacketBlockChange packet = new S23PacketBlockChange(x, y, z, world); + packet.field_148883_d = Blocks.air; +@@ -463,11 +466,11 @@ + Block block = world.getBlock(x, y, z); + int blockMetadata = world.getBlockMetadata(x, y, z); + BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(x, y, z, world, block, blockMetadata, entityPlayer); +- event.setCanceled(preCancelEvent); ++ // event.setCanceled(preCancelEvent); // Cauldron + MinecraftForge.EVENT_BUS.post(event); + + // Handle if the event is canceled +- if (event.isCanceled()) ++ if (event.isCanceled() && !(entityPlayer instanceof FakePlayer)) // Cauldron - don't send packets to fakeplayers + { + // Let the client know the block still exists + entityPlayer.playerNetServerHandler.sendPacket(new S23PacketBlockChange(x, y, z, world)); diff --git a/patches/net/minecraftforge/common/ForgeVersion.java.patch b/patches/net/minecraftforge/common/ForgeVersion.java.patch new file mode 100644 index 0000000..e3b4c01 --- /dev/null +++ b/patches/net/minecraftforge/common/ForgeVersion.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/net/minecraftforge/common/ForgeVersion.java ++++ ../src-work/minecraft/net/minecraftforge/common/ForgeVersion.java +@@ -25,7 +25,7 @@ + //This number is incremented every time a interface changes or new major feature is added, and reset every Minecraft version + public static final int revisionVersion = 2; + //This number is incremented every time Jenkins builds Forge, and never reset. Should always be 0 in the repo code. +- public static final int buildVersion = 0; ++ public static final int buildVersion = 1291; // Cauldron + + private static Status status = PENDING; + private static String target = null; diff --git a/patches/net/minecraftforge/common/WorldSpecificSaveHandler.java.patch b/patches/net/minecraftforge/common/WorldSpecificSaveHandler.java.patch new file mode 100644 index 0000000..0b0caec --- /dev/null +++ b/patches/net/minecraftforge/common/WorldSpecificSaveHandler.java.patch @@ -0,0 +1,22 @@ +--- ../src-base/minecraft/net/minecraftforge/common/WorldSpecificSaveHandler.java ++++ ../src-work/minecraft/net/minecraftforge/common/WorldSpecificSaveHandler.java +@@ -1,6 +1,7 @@ + package net.minecraftforge.common; + + import java.io.File; ++import java.util.UUID; + + import net.minecraft.world.chunk.storage.IChunkLoader; + import net.minecraft.world.storage.IPlayerFileData; +@@ -43,4 +44,11 @@ + return new File(dataDir, name + ".dat"); + } + ++ // Cauldron start ++ @Override ++ public UUID getUUID() ++ { ++ return parent.getUUID(); ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch b/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch new file mode 100644 index 0000000..e9223dd --- /dev/null +++ b/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch @@ -0,0 +1,34 @@ +--- ../src-base/minecraft/net/minecraftforge/common/chunkio/ChunkIOProvider.java ++++ ../src-work/minecraft/net/minecraftforge/common/chunkio/ChunkIOProvider.java +@@ -9,6 +9,9 @@ + import java.io.IOException; + import java.util.concurrent.atomic.AtomicInteger; + ++import org.bukkit.Server; ++import org.bukkit.craftbukkit.util.LongHash; ++ + class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider { + private final AtomicInteger threadNumber = new AtomicInteger(1); + +@@ -41,13 +44,20 @@ + queuedChunk.loader.loadEntities(queuedChunk.world, queuedChunk.compound.getCompoundTag("Level"), chunk); + MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, queuedChunk.compound)); // Don't call ChunkDataEvent.Load async + chunk.lastSaveTime = queuedChunk.provider.worldObj.getTotalWorldTime(); +- queuedChunk.provider.loadedChunkHashMap.add(ChunkCoordIntPair.chunkXZ2Int(queuedChunk.x, queuedChunk.z), chunk); ++ queuedChunk.provider.loadedChunkHashMap.put(LongHash.toLong(queuedChunk.x, queuedChunk.z), chunk); + queuedChunk.provider.loadedChunks.add(chunk); + chunk.onChunkLoad(); + + if (queuedChunk.provider.currentChunkProvider != null) { ++ queuedChunk.provider.worldObj.timings.syncChunkLoadStructuresTimer.startTiming(); // Spigot + queuedChunk.provider.currentChunkProvider.recreateStructures(queuedChunk.x, queuedChunk.z); ++ queuedChunk.provider.worldObj.timings.syncChunkLoadStructuresTimer.stopTiming(); // Spigot + } ++ ++ Server server = queuedChunk.provider.worldObj.getServer(); ++ if (server != null) { ++ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(chunk.bukkitChunk, false)); ++ } + + chunk.populateChunk(queuedChunk.provider, queuedChunk.provider, queuedChunk.x, queuedChunk.z); + } diff --git a/patches/net/minecraftforge/common/network/ForgeNetworkHandler.java.patch b/patches/net/minecraftforge/common/network/ForgeNetworkHandler.java.patch new file mode 100644 index 0000000..97cc2f5 --- /dev/null +++ b/patches/net/minecraftforge/common/network/ForgeNetworkHandler.java.patch @@ -0,0 +1,12 @@ +--- ../src-base/minecraft/net/minecraftforge/common/network/ForgeNetworkHandler.java ++++ ../src-work/minecraft/net/minecraftforge/common/network/ForgeNetworkHandler.java +@@ -35,4 +35,9 @@ + clientChannel.pipeline().addAfter(handlerName, "DimensionHandler", new DimensionMessageHandler()); + clientChannel.pipeline().addAfter(handlerName, "FluidIdRegistryHandler", new FluidIdRegistryMessageHandler()); + } ++ ++ public static FMLEmbeddedChannel getServerChannel() ++ { ++ return channelPair.get(Side.SERVER); ++ } + } diff --git a/patches/net/minecraftforge/common/util/EnumHelper.java.patch b/patches/net/minecraftforge/common/util/EnumHelper.java.patch new file mode 100644 index 0000000..bd83a8a --- /dev/null +++ b/patches/net/minecraftforge/common/util/EnumHelper.java.patch @@ -0,0 +1,195 @@ +--- ../src-base/minecraft/net/minecraftforge/common/util/EnumHelper.java ++++ ../src-work/minecraft/net/minecraftforge/common/util/EnumHelper.java +@@ -21,7 +21,23 @@ + import net.minecraft.world.EnumSkyBlock; + import net.minecraft.world.gen.structure.StructureStrongholdPieces.Stronghold.Door; + import net.minecraftforge.classloading.FMLForgePlugin; ++// Cauldron start ++import cpw.mods.fml.relauncher.ReflectionHelper; ++import net.minecraft.inventory.IInventory; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.tileentity.TileEntity; + ++import org.apache.logging.log4j.Level; ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++import org.bukkit.World; ++import org.bukkit.WorldType; ++import org.bukkit.block.Biome; ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.entity.EntityType; ++import org.bukkit.event.inventory.InventoryType; ++// Cauldron end ++ + public class EnumHelper + { + private static Object reflectionFactory = null; +@@ -30,6 +46,7 @@ + private static Method newFieldAccessor = null; + private static Method fieldAccessorSet = null; + private static boolean isSetup = false; ++ private static final Logger logger = LogManager.getLogger(); + + //Some enums are decompiled with extra arguments, so lets check for that + @SuppressWarnings("rawtypes") +@@ -51,6 +68,73 @@ + {EnumRarity.class, EnumChatFormatting.class, String.class} + }; + ++ // Cauldron start ++ public static Biome addBukkitBiome(String name) ++ { ++ return (Biome)addEnum(Biome.class, name, new Class[0], new Object[0]); ++ } ++ ++ public static World.Environment addBukkitEnvironment(int id, String name) ++ { ++ if (!isSetup) ++ { ++ setup(); ++ } ++ ++ return (World.Environment)addEnum(World.Environment.class, name, new Class[] { Integer.TYPE }, new Object[] { Integer.valueOf(id) }); ++ } ++ ++ public static WorldType addBukkitWorldType(String name) ++ { ++ if (!isSetup) ++ { ++ setup(); ++ } ++ ++ WorldType worldType = addEnum(WorldType.class, name, new Class [] { String.class }, new Object[] { name }); ++ Map BY_NAME = ReflectionHelper.getPrivateValue(WorldType.class, null, "BY_NAME"); ++ BY_NAME.put(name.toUpperCase(), worldType); ++ ++ return worldType; ++ } ++ ++ public static EntityType addBukkitEntityType(String name, Class clazz, int typeId, boolean independent) { ++ String entityType = name.replace("-", "_").toUpperCase(); ++ EntityType bukkitType = addEnum(EntityType.class, entityType, new Class[] { String.class, Class.class, Integer.TYPE, Boolean.TYPE }, new Object[] { name, clazz, typeId, independent }); ++ ++ Map NAME_MAP = ReflectionHelper.getPrivateValue(EntityType.class, null, "NAME_MAP"); ++ Map ID_MAP = ReflectionHelper.getPrivateValue(EntityType.class, null, "ID_MAP"); ++ ++ NAME_MAP.put(name.toLowerCase(), bukkitType); ++ ID_MAP.put((short)typeId, bukkitType); ++ ++ ++ return bukkitType; ++ } ++ ++ public static InventoryType addInventoryType(TileEntity tileentity) ++ { ++ if (!IInventory.class.isAssignableFrom(tileentity.getClass())) return null; ++ String id = (String)TileEntity.classToNameMap.get(tileentity.getClass()); ++ ++ try ++ { ++ IInventory teInv = (IInventory)tileentity; ++ int size = teInv.getSizeInventory(); ++ return addEnum(org.bukkit.event.inventory.InventoryType.class, id, new Class[]{Integer.TYPE, String.class}, new Object[]{size, id}); ++ } ++ catch (Throwable e) ++ { ++ if (MinecraftServer.getServer().tileEntityConfig.enableTEInventoryWarning.getValue()) ++ { ++ logger.log(Level.WARN, "Could not create inventory type " + tileentity.getClass().getName() + " Exception: " + e.toString()); ++ logger.log(Level.WARN, "Could not determine default inventory size for type " + tileentity.getClass().getName() + " using size of 9"); ++ } ++ return addEnum(org.bukkit.event.inventory.InventoryType.class, id, new Class[]{Integer.TYPE, String.class}, new Object[]{9, id}); ++ } ++ } ++ // Cauldron end ++ + public static EnumAction addAction(String name) + { + return addEnum(EnumAction.class, name); +@@ -280,6 +364,86 @@ + } + } + ++ // Cauldron start ++ @SuppressWarnings("unchecked") ++ public static > T replaceEnum(Class enumType, String enumName, int ordinal, Class[] paramTypes, Object[] paramValues) ++ { ++ if (!isSetup) ++ { ++ setup(); ++ } ++ ++ Field valuesField = null; ++ Field[] fields = enumType.getDeclaredFields(); ++ ++ for (Field field : fields) ++ { ++ String name = field.getName(); ++ if (name.equals("$VALUES") || name.equals("ENUM$VALUES")) //Added 'ENUM$VALUES' because Eclipse's internal compiler doesn't follow standards ++ { ++ valuesField = field; ++ break; ++ } ++ } ++ ++ int flags = (FMLForgePlugin.RUNTIME_DEOBF ? Modifier.PUBLIC : Modifier.PRIVATE) | Modifier.STATIC | Modifier.FINAL | 0x1000 /*SYNTHETIC*/; ++ if (valuesField == null) ++ { ++ String valueType = String.format("[L%s;", enumType.getName().replace('.', '/')); ++ ++ for (Field field : fields) ++ { ++ if ((field.getModifiers() & flags) == flags && ++ field.getType().getName().replace('.', '/').equals(valueType)) //Apparently some JVMs return .'s and some don't.. ++ { ++ valuesField = field; ++ break; ++ } ++ } ++ } ++ ++ if (valuesField == null) ++ { ++ FMLLog.severe("Could not find $VALUES field for enum: %s", enumType.getName()); ++ FMLLog.severe("Runtime Deobf: %s", FMLForgePlugin.RUNTIME_DEOBF); ++ FMLLog.severe("Flags: %s", String.format("%16s", Integer.toBinaryString(flags)).replace(' ', '0')); ++ FMLLog.severe("Fields:"); ++ for (Field field : fields) ++ { ++ String mods = String.format("%16s", Integer.toBinaryString(field.getModifiers())).replace(' ', '0'); ++ FMLLog.severe(" %s %s: %s", mods, field.getName(), field.getType().getName()); ++ } ++ return null; ++ } ++ ++ valuesField.setAccessible(true); ++ try ++ { ++ Enum[] previousValues = (Enum[])(Enum[])valuesField.get(enumType); ++ Enum[] newValues = new Enum[previousValues.length]; ++ Enum newValue = null; ++ for (Enum enumValue : previousValues) ++ { ++ if (enumValue.ordinal() == ordinal) ++ { ++ newValue = makeEnum(enumType, enumName, ordinal, paramTypes, paramValues); ++ newValues[enumValue.ordinal()] = newValue; ++ } ++ else newValues[enumValue.ordinal()] = enumValue; ++ } ++ List values = new ArrayList(Arrays.asList(newValues)); ++ setFailsafeFieldValue(valuesField, null, values.toArray((Enum[])(Enum[])Array.newInstance(enumType, 0))); ++ cleanEnumCache(enumType); ++ return (T) newValue; ++ } ++ catch (Exception e) ++ { ++ e.printStackTrace(); ++ throw new RuntimeException(e.getMessage(), e); ++ } ++ } ++ // Cauldron end ++ + static + { + if (!isSetup) diff --git a/patches/net/minecraftforge/common/util/FakePlayerFactory.java.patch b/patches/net/minecraftforge/common/util/FakePlayerFactory.java.patch new file mode 100644 index 0000000..594cfb7 --- /dev/null +++ b/patches/net/minecraftforge/common/util/FakePlayerFactory.java.patch @@ -0,0 +1,31 @@ +--- ../src-base/minecraft/net/minecraftforge/common/util/FakePlayerFactory.java ++++ ../src-work/minecraft/net/minecraftforge/common/util/FakePlayerFactory.java +@@ -35,12 +35,24 @@ + */ + public static FakePlayer get(WorldServer world, GameProfile username) + { +- if (!fakePlayers.containsKey(username)) ++ // Cauldron start - Refactored below to avoid a hashCode check with a null GameProfile ID ++ if (username == null || username.getName() == null) return null; ++ ++ for (Map.Entry mapEntry : fakePlayers.entrySet()) + { +- FakePlayer fakePlayer = new FakePlayer(world, username); +- fakePlayers.put(username, fakePlayer); ++ GameProfile gameprofile = mapEntry.getKey(); ++ if (gameprofile.getName().equals(username.getName())) ++ { ++ return mapEntry.getValue(); ++ } + } +- ++ FakePlayer fakePlayer = new FakePlayer(world, username); ++ if (username.getId() == null) // GameProfile hashCode check will fail with a null ID ++ { ++ username = new GameProfile(UUID.randomUUID(), username.getName()); // Create new GameProfile with random UUID ++ } ++ // Cauldron end ++ fakePlayers.put(username, fakePlayer); + return fakePlayers.get(username); + } + diff --git a/patches/net/minecraftforge/event/entity/living/LivingSpawnEvent.java.patch b/patches/net/minecraftforge/event/entity/living/LivingSpawnEvent.java.patch new file mode 100644 index 0000000..862a5da --- /dev/null +++ b/patches/net/minecraftforge/event/entity/living/LivingSpawnEvent.java.patch @@ -0,0 +1,18 @@ +--- ../src-base/minecraft/net/minecraftforge/event/entity/living/LivingSpawnEvent.java ++++ ../src-work/minecraft/net/minecraftforge/event/entity/living/LivingSpawnEvent.java +@@ -48,6 +48,7 @@ + public CheckSpawn(EntityLiving entity, World world, float x, float y, float z) + { + super(entity, world, x, y, z); ++ entity.spawnReason = "natural"; // Cauldron - used to handle CraftBukkit's SpawnReason with CustomSpawners + } + } + +@@ -71,6 +72,7 @@ + public SpecialSpawn(EntityLiving entity, World world, float x, float y, float z) + { + super(entity, world, x, y, z); ++ entity.spawnReason = "spawner"; // Cauldron - used to handle CraftBukkit's SpawnReason with CustomSpawners + } + } + diff --git a/patches/net/minecraftforge/event/world/BlockEvent.java.patch b/patches/net/minecraftforge/event/world/BlockEvent.java.patch new file mode 100644 index 0000000..630d583 --- /dev/null +++ b/patches/net/minecraftforge/event/world/BlockEvent.java.patch @@ -0,0 +1,39 @@ +--- ../src-base/minecraft/net/minecraftforge/event/world/BlockEvent.java ++++ ../src-work/minecraft/net/minecraftforge/event/world/BlockEvent.java +@@ -16,6 +16,11 @@ + import net.minecraftforge.common.ForgeHooks; + import net.minecraftforge.common.util.BlockSnapshot; + ++// Cauldron start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import net.minecraft.entity.player.EntityPlayerMP; ++// Cauldron end ++ + public class BlockEvent extends Event { + private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("forge.debugBlockEvent", "false")); + +@@ -80,17 +85,18 @@ + super(x, y, z, world, block, blockMetadata); + this.player = player; + +- if (block == null || !ForgeHooks.canHarvestBlock(block, player, blockMetadata) || // Handle empty block or player unable to break block scenario +- block.canSilkHarvest(world, player, x, y, z, blockMetadata) && EnchantmentHelper.getSilkTouchModifier(player)) // If the block is being silk harvested, the exp dropped is 0 ++ // Cauldron start - handle event on bukkit side ++ org.bukkit.event.block.BlockBreakEvent bukkitEvent = CraftEventFactory.callBlockBreakEvent(world, x, y, z, block, blockMetadata, ++ (EntityPlayerMP) player); ++ if (bukkitEvent.isCancelled()) + { +- this.exp = 0; ++ this.setCanceled(true); + } + else + { +- int meta = block.getDamageValue(world, x, y, z); +- int bonusLevel = EnchantmentHelper.getFortuneModifier(player); +- this.exp = block.getExpDrop(world, meta, bonusLevel); ++ this.exp = bukkitEvent.getExpToDrop(); + } ++ // Cauldron end + } + + public EntityPlayer getPlayer() diff --git a/patches/net/minecraftforge/oredict/OreDictionary.java.patch b/patches/net/minecraftforge/oredict/OreDictionary.java.patch new file mode 100644 index 0000000..05f2f2a --- /dev/null +++ b/patches/net/minecraftforge/oredict/OreDictionary.java.patch @@ -0,0 +1,20 @@ +--- ../src-base/minecraft/net/minecraftforge/oredict/OreDictionary.java ++++ ../src-work/minecraft/net/minecraftforge/oredict/OreDictionary.java +@@ -216,7 +216,7 @@ + { + ShapedRecipes recipe = (ShapedRecipes)obj; + ItemStack output = recipe.getRecipeOutput(); +- if (output != null && containsMatch(false, exclusions, output)) ++ if ((output != null && containsMatch(false, exclusions, output)) || output == null) // Cauldron - fixes NPE's with null recipes being added to forge + { + continue; + } +@@ -231,7 +231,7 @@ + { + ShapelessRecipes recipe = (ShapelessRecipes)obj; + ItemStack output = recipe.getRecipeOutput(); +- if (output != null && containsMatch(false, exclusions, output)) ++ if ((output != null && containsMatch(false, exclusions, output)) || output == null) // Cauldron - fixes NPE's with null recipes being added to forge + { + continue; + } diff --git a/patches/net/minecraftforge/oredict/ShapedOreRecipe.java.patch b/patches/net/minecraftforge/oredict/ShapedOreRecipe.java.patch new file mode 100644 index 0000000..f6bae3c --- /dev/null +++ b/patches/net/minecraftforge/oredict/ShapedOreRecipe.java.patch @@ -0,0 +1,51 @@ +--- ../src-base/minecraft/net/minecraftforge/oredict/ShapedOreRecipe.java ++++ ../src-work/minecraft/net/minecraftforge/oredict/ShapedOreRecipe.java +@@ -6,6 +6,10 @@ + import java.util.Map; + import java.util.Map.Entry; + ++// Cauldron start ++import org.bukkit.inventory.Recipe; ++// Cauldron end ++ + import net.minecraft.block.Block; + import net.minecraft.item.crafting.IRecipe; + import net.minecraft.inventory.InventoryCrafting; +@@ -13,6 +17,7 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.item.crafting.ShapedRecipes; + import net.minecraft.world.World; ++import net.minecraftforge.cauldron.inventory.CustomModRecipe; + + public class ShapedOreRecipe implements IRecipe + { +@@ -25,6 +30,7 @@ + private int width = 0; + private int height = 0; + private boolean mirrored = true; ++ private ShapedRecipes vanillaRecipe = null; // Cauldron - bukkit compatibility + + public ShapedOreRecipe(Block result, Object... recipe){ this(new ItemStack(result), recipe); } + public ShapedOreRecipe(Item result, Object... recipe){ this(new ItemStack(result), recipe); } +@@ -127,6 +133,7 @@ + + ShapedOreRecipe(ShapedRecipes recipe, Map replacements) + { ++ vanillaRecipe = recipe; // Cauldron - bukkit compatibility + output = recipe.getRecipeOutput(); + width = recipe.recipeWidth; + height = recipe.recipeHeight; +@@ -255,4 +262,13 @@ + { + return this.input; + } ++ ++ // Cauldron start - required for Bukkit API ++ @Override ++ public Recipe toBukkitRecipe() { ++ if (vanillaRecipe != null) ++ return vanillaRecipe.toBukkitRecipe(); ++ return new CustomModRecipe(this); ++ } ++ // Cauldron end + } diff --git a/patches/net/minecraftforge/oredict/ShapelessOreRecipe.java.patch b/patches/net/minecraftforge/oredict/ShapelessOreRecipe.java.patch new file mode 100644 index 0000000..ae00843 --- /dev/null +++ b/patches/net/minecraftforge/oredict/ShapelessOreRecipe.java.patch @@ -0,0 +1,49 @@ +--- ../src-base/minecraft/net/minecraftforge/oredict/ShapelessOreRecipe.java ++++ ../src-work/minecraft/net/minecraftforge/oredict/ShapelessOreRecipe.java +@@ -6,6 +6,10 @@ + import java.util.Map.Entry; + import java.util.List; + ++// Cauldron start ++import org.bukkit.inventory.Recipe; ++// Cauldron end ++ + import net.minecraft.block.Block; + import net.minecraft.item.crafting.IRecipe; + import net.minecraft.inventory.InventoryCrafting; +@@ -13,11 +17,13 @@ + import net.minecraft.item.ItemStack; + import net.minecraft.item.crafting.ShapelessRecipes; + import net.minecraft.world.World; ++import net.minecraftforge.cauldron.inventory.CustomModRecipe; + + public class ShapelessOreRecipe implements IRecipe + { + private ItemStack output = null; + private ArrayList input = new ArrayList(); ++ private ShapelessRecipes vanillaRecipe = null; // Cauldron - bukkit compatibility + + public ShapelessOreRecipe(Block result, Object... recipe){ this(new ItemStack(result), recipe); } + public ShapelessOreRecipe(Item result, Object... recipe){ this(new ItemStack(result), recipe); } +@@ -59,6 +65,7 @@ + @SuppressWarnings("unchecked") + ShapelessOreRecipe(ShapelessRecipes recipe, Map replacements) + { ++ vanillaRecipe = recipe; // Cauldron - bukkit compatibility + output = recipe.getRecipeOutput(); + + for(ItemStack ingred : ((List)recipe.recipeItems)) +@@ -146,4 +153,13 @@ + { + return this.input; + } ++ ++ // Cauldron start - required for Bukkit API ++ @Override ++ public Recipe toBukkitRecipe() { ++ if (vanillaRecipe != null) ++ return vanillaRecipe.toBukkitRecipe(); ++ return new CustomModRecipe(this); ++ } ++ // Cauldron end + } diff --git a/patches/org/bukkit/Bukkit.java.patch b/patches/org/bukkit/Bukkit.java.patch new file mode 100644 index 0000000..fe66c31 --- /dev/null +++ b/patches/org/bukkit/Bukkit.java.patch @@ -0,0 +1,10 @@ +--- ../src-base/minecraft/org/bukkit/Bukkit.java ++++ ../src-work/minecraft/org/bukkit/Bukkit.java +@@ -301,6 +301,7 @@ + */ + public static void reload() { + server.reload(); ++ org.spigotmc.CustomTimingsHandler.reload(); // Spigot + } + + /** diff --git a/patches/org/bukkit/Effect.java.patch b/patches/org/bukkit/Effect.java.patch new file mode 100644 index 0000000..0de855a --- /dev/null +++ b/patches/org/bukkit/Effect.java.patch @@ -0,0 +1,259 @@ +--- ../src-base/minecraft/org/bukkit/Effect.java ++++ ../src-work/minecraft/org/bukkit/Effect.java +@@ -5,6 +5,7 @@ + import com.google.common.collect.Maps; + + import org.bukkit.block.BlockFace; ++import org.bukkit.material.MaterialData; + import org.bukkit.potion.Potion; + + /** +@@ -79,27 +80,183 @@ + /** + * The flames seen on a mobspawner; a visual effect. + */ +- MOBSPAWNER_FLAMES(2004, Type.VISUAL); ++ MOBSPAWNER_FLAMES(2004, Type.VISUAL), ++ /** ++ * The spark that comes off a fireworks ++ */ ++ FIREWORKS_SPARK("fireworksSpark", Type.PARTICLE), ++ /** ++ * Critical hit particles ++ */ ++ CRIT("crit", Type.PARTICLE), ++ /** ++ * Blue critical hit particles ++ */ ++ MAGIC_CRIT("magicCrit", Type.PARTICLE), ++ /** ++ * Multicolored potion effect particles ++ */ ++ POTION_SWIRL("mobSpell", Type.PARTICLE), ++ /** ++ * Multicolored potion effect particles that are slightly transparent ++ */ ++ POTION_SWIRL_TRANSPARENT("mobSpellAmbient", Type.PARTICLE), ++ /** ++ * A puff of white potion swirls ++ */ ++ SPELL("spell", Type.PARTICLE), ++ /** ++ * A puff of white stars ++ */ ++ INSTANT_SPELL("instantSpell", Type.PARTICLE), ++ /** ++ * A puff of purple particles ++ */ ++ WITCH_MAGIC("witchMagic", Type.PARTICLE), ++ /** ++ * The note that appears above note blocks ++ */ ++ NOTE("note", Type.PARTICLE), ++ /** ++ * The particles shown at nether portals ++ */ ++ PORTAL("portal", Type.PARTICLE), ++ /** ++ * The symbols that fly towards the enchantment table ++ */ ++ FLYING_GLYPH("enchantmenttable", Type.PARTICLE), ++ /** ++ * Fire particles ++ */ ++ FLAME("flame", Type.PARTICLE), ++ /** ++ * The particles that pop out of lava ++ */ ++ LAVA_POP("lava", Type.PARTICLE), ++ /** ++ * A small gray square ++ */ ++ FOOTSTEP("footstep", Type.PARTICLE), ++ /** ++ * Water particles ++ */ ++ SPLASH("splash", Type.PARTICLE), ++ /** ++ * Smoke particles ++ */ ++ PARTICLE_SMOKE("smoke", Type.PARTICLE), ++ /** ++ * The biggest explosion particle effect ++ */ ++ EXPLOSION_HUGE("hugeexplosion", Type.PARTICLE), ++ /** ++ * A larger version of the explode particle ++ */ ++ EXPLOSION_LARGE("largeexplode", Type.PARTICLE), ++ /** ++ * Explosion particles ++ */ ++ EXPLOSION("explode", Type.PARTICLE), ++ /** ++ * Small gray particles ++ */ ++ VOID_FOG("depthsuspend", Type.PARTICLE), ++ /** ++ * Small gray particles ++ */ ++ SMALL_SMOKE("townaura", Type.PARTICLE), ++ /** ++ * A puff of white smoke ++ */ ++ CLOUD("cloud", Type.PARTICLE), ++ /** ++ * Multicolored dust particles ++ */ ++ COLOURED_DUST("reddust", Type.PARTICLE), ++ /** ++ * Snowball breaking ++ */ ++ SNOWBALL_BREAK("snowballpoof", Type.PARTICLE), ++ /** ++ * The water drip particle that appears on blocks under water ++ */ ++ WATERDRIP("dripWater", Type.PARTICLE), ++ /** ++ * The lava drip particle that appears on blocks under lava ++ */ ++ LAVADRIP("dripLava", Type.PARTICLE), ++ /** ++ * White particles ++ */ ++ SNOW_SHOVEL("snowshovel", Type.PARTICLE), ++ /** ++ * The particle shown when a slime jumps ++ */ ++ SLIME("slime", Type.PARTICLE), ++ /** ++ * The particle that appears when breading animals ++ */ ++ HEART("heart", Type.PARTICLE), ++ /** ++ * The particle that appears when hitting a villager ++ */ ++ VILLAGER_THUNDERCLOUD("angryVillager", Type.PARTICLE), ++ /** ++ * The particle that appears when trading with a villager ++ */ ++ HAPPY_VILLAGER("happyVillager", Type.PARTICLE), ++ /** ++ * The particles generated when a tool breaks. ++ * This particle requires a Material so that the client can select the correct texture. ++ */ ++ ITEM_BREAK("iconcrack", Type.PARTICLE, Material.class), ++ /** ++ * The particles generated while breaking a block. ++ * This particle requires a Material and data value so that the client can select the correct texture. ++ */ ++ TILE_BREAK("blockcrack", Type.PARTICLE, MaterialData.class), ++ /** ++ * The particles generated while sprinting a block ++ * This particle requires a Material and data value so that the client can select the correct texture. ++ */ ++ TILE_DUST("blockdust", Type.PARTICLE, MaterialData.class); + + private final int id; + private final Type type; + private final Class data; + private static final Map BY_ID = Maps.newHashMap(); ++ private static final Map BY_NAME = Maps.newHashMap(); ++ private final String particleName; + +- Effect(int id, Type type) { ++ private Effect(int id, Type type) { + this(id,type,null); + } + +- Effect(int id, Type type, Class data) { ++ private Effect(int id, Type type, Class data) { + this.id = id; + this.type = type; + this.data = data; ++ particleName = null; + } + ++ private Effect(String particleName, Type type, Class data) { ++ this.particleName = particleName; ++ this.type = type; ++ id = 0; ++ this.data = data; ++ } ++ ++ private Effect(String particleName, Type type) { ++ this.particleName = particleName; ++ this.type = type; ++ id = 0; ++ this.data = null; ++ } ++ + /** + * Gets the ID for this effect. + * +- * @return ID of this effect ++ * @return if this Effect isn't of type PARTICLE it returns ID of this effect + * @deprecated Magic value + */ + @Deprecated +@@ -108,6 +265,15 @@ + } + + /** ++ * Returns the effect's name. This returns null if the effect is not a particle ++ * ++ * @return The effect's name ++ */ ++ public String getName() { ++ return particleName; ++ } ++ ++ /** + * @return The type of the effect. + */ + public Type getType() { +@@ -115,8 +281,7 @@ + } + + /** +- * @return The class which represents data for this effect, or null if +- * none ++ * @return if this Effect isn't of type PARTICLE it returns the class which represents data for this effect, or null if none + */ + public Class getData() { + return this.data; +@@ -136,12 +301,32 @@ + + static { + for (Effect effect : values()) { +- BY_ID.put(effect.id, effect); ++ if (effect.type != Type.PARTICLE) { ++ BY_ID.put(effect.id, effect); ++ } + } + } + + /** ++ * Gets the Effect associated with the given name. ++ * ++ * @param name name of the Effect to return ++ * @return Effect with the given name ++ */ ++ public static Effect getByName(String name) { ++ return BY_NAME.get(name); ++ } ++ ++ static { ++ for (Effect effect : values()) { ++ if (effect.type == Type.PARTICLE) { ++ BY_NAME.put(effect.particleName, effect); ++ } ++ } ++ } ++ ++ /** + * Represents the type of an effect. + */ +- public enum Type {SOUND, VISUAL} ++ public enum Type {SOUND, VISUAL, PARTICLE} + } diff --git a/patches/org/bukkit/GameMode.java.patch b/patches/org/bukkit/GameMode.java.patch new file mode 100644 index 0000000..2547dd4 --- /dev/null +++ b/patches/org/bukkit/GameMode.java.patch @@ -0,0 +1,16 @@ +--- ../src-base/minecraft/org/bukkit/GameMode.java ++++ ../src-work/minecraft/org/bukkit/GameMode.java +@@ -11,7 +11,13 @@ + * have + */ + public enum GameMode { ++ // Cauldron start - FakePlayers do not set their gametype and use the default + /** ++ * Default mode ++ */ ++ NOT_SET(-1), ++ // Cauldron end ++ /** + * Creative mode may fly, build instantly, become invulnerable and create + * free items. + */ diff --git a/patches/org/bukkit/Material.java.patch b/patches/org/bukkit/Material.java.patch new file mode 100644 index 0000000..850d532 --- /dev/null +++ b/patches/org/bukkit/Material.java.patch @@ -0,0 +1,272 @@ +--- ../src-base/minecraft/org/bukkit/Material.java ++++ ../src-work/minecraft/org/bukkit/Material.java +@@ -3,6 +3,22 @@ + import java.lang.reflect.Constructor; + import java.util.Map; + ++// Cauldron start ++import java.lang.reflect.Array; ++import java.lang.reflect.Constructor; ++import java.lang.reflect.Field; ++import java.lang.reflect.Method; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.List; ++ ++import org.bukkit.inventory.ItemStack; ++ ++import net.minecraftforge.common.util.EnumHelper; ++import net.minecraftforge.cauldron.api.inventory.BukkitOreDictionary; ++import net.minecraftforge.cauldron.api.inventory.OreDictionaryEntry; ++// Cauldron end ++ + import org.apache.commons.lang.Validate; + import org.bukkit.map.MapView; + import org.bukkit.material.Bed; +@@ -418,14 +434,30 @@ + private final int id; + private final Constructor ctor; + private static Material[] byId = new Material[383]; +- private final static Map BY_NAME = Maps.newHashMap(); ++ private static Map BY_NAME = Maps.newHashMap(); // Cauldron - remove final + private final int maxStack; + private final short durability; ++ // Cauldron start ++ private static Object reflectionFactory; ++ private static Method newConstructorAccessor; ++ private static Method newInstance; ++ private static Method newFieldAccessor; ++ private static Method fieldAccessorSet; ++ private static boolean isSetup; ++ private boolean isForgeBlock = false; ++ // Cauldron end + + private Material(final int id) { + this(id, 64); + } + ++ // Cauldron start - constructor used to set if the Material is a block or not ++ private Material(final int id, boolean flag) { ++ this(id, 64); ++ this.isForgeBlock = flag; ++ } ++ // Cauldron end ++ + private Material(final int id, final int stack) { + this(id, stack, MaterialData.class); + } +@@ -526,7 +558,7 @@ + * @return true if this material is a block + */ + public boolean isBlock() { +- return id < 256; ++ return id < 256 || isForgeBlock; // Cauldron + } + + /** +@@ -615,16 +647,202 @@ + } catch (NumberFormatException ex) {} + + if (result == null) { +- String filtered = name.toUpperCase(); +- +- filtered = filtered.replaceAll("\\s+", "_").replaceAll("\\W", ""); ++ // Cauldron start - extract to normalizeName() ++ String filtered = normalizeName(name); + result = BY_NAME.get(filtered); ++ // Cauldron end + } + ++ // Cauldron start - Try the ore dictionary ++ if (result == null) { ++ BukkitOreDictionary dict = net.minecraftforge.cauldron.api.Cauldron.getOreDictionary(); ++ OreDictionaryEntry entry = dict.getOreEntry(name); ++ if (entry != null) { ++ List items = dict.getDefinitions(entry); ++ if (items.size() > 0) { ++ // TODO check sanity on multiple item results ++ ItemStack item = items.get(0); ++ if (item.getDurability() == 0 || item.getDurability() == Short.MAX_VALUE) { ++ result = item.getType(); ++ } else { ++ // bad! we have an item with data! ++ } ++ } ++ } ++ } ++ // Cauldron end ++ + return result; + } + ++ /* =============================== Cauldron START ============================= */ ++ ++ // use a normalize() function to ensure it is accessible after a round-trip ++ public static String normalizeName(String name) { ++ return name.toUpperCase().replaceAll("(:|\\s)", "_").replaceAll("\\W", ""); ++ } ++ ++ public static Material addMaterial(int id, boolean isBlock) ++ { ++ return addMaterial(id, "X" + String.valueOf(id), isBlock); ++ } ++ ++ public static Material addMaterial(int id, String name, boolean isBlock) { ++ if (byId[id] == null) { ++ String materialName = normalizeName(name); ++ Material material = (Material) EnumHelper.addEnum(Material.class, materialName, new Class[]{Integer.TYPE, Boolean.TYPE}, new Object[]{Integer.valueOf(id), isBlock}); ++ byId[id] = material; ++ BY_NAME.put(materialName, material); ++ BY_NAME.put("X" + String.valueOf(id), material); ++ return material; ++ } ++ return null; ++ } ++ ++ public static void setMaterialName(int id, String name, boolean flag) { ++ String materialName = normalizeName(name); ++ ++ if (byId[id] == null) ++ { ++ addMaterial(id, materialName, flag); ++ } ++ else // replace existing enum ++ { ++ /* TODO: find out how to do this with Forge's EnumHelper (addEnum?) - used for enabling descriptive (vs numeric) Material names ++ Material material = getMaterial(id); ++ BY_NAME.remove(material); ++ Material newMaterial = EnumHelper.replaceEnum(Material.class, material_name, material.ordinal(), new Class[] { Integer.TYPE }, new Object[] { Integer.valueOf(id) }); ++ if (newMaterial == null) ++ System.out.println("Error replacing Material " + name + " with id " + id); ++ else { ++ byId[id] = newMaterial; ++ BY_NAME.put(material_name, newMaterial); ++ } ++ */ ++ } ++ } ++ ++ private static void setup() ++ { ++ if (isSetup) ++ { ++ return; ++ } ++ try { ++ Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory", new Class[0]); ++ reflectionFactory = getReflectionFactory.invoke(null, new Object[0]); ++ newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", new Class[] { Constructor.class }); ++ newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", new Class[] { Object[].class }); ++ newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", new Class[] { Field.class, Boolean.TYPE }); ++ fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", new Class[] { Object.class, Object.class }); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } ++ ++ isSetup = true; ++ } ++ ++ private static Object getConstructorAccessor(Class enumClass, Class[] additionalParameterTypes) throws Exception { ++ Class[] parameterTypes = null; ++ ++ parameterTypes = new Class[additionalParameterTypes.length + 2]; ++ parameterTypes[0] = String.class; ++ parameterTypes[1] = Integer.TYPE; ++ System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length); ++ ++ return newConstructorAccessor.invoke(reflectionFactory, new Object[] { enumClass.getDeclaredConstructor(parameterTypes) }); ++ } ++ ++ private static > T makeEnum(Class enumClass, String value, int ordinal, Class[] additionalTypes, Object[] additionalValues) throws Exception { ++ Object[] parms = null; ++ ++ parms = new Object[additionalValues.length + 2]; ++ parms[0] = value; ++ parms[1] = Integer.valueOf(ordinal); ++ System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length); ++ ++ return (T)enumClass.cast(newInstance.invoke(getConstructorAccessor(enumClass, additionalTypes), new Object[] { parms })); ++ } ++ ++ private static void setFailsafeFieldValue(Field field, Object target, Object value) throws Exception { ++ field.setAccessible(true); ++ Field modifiersField = Field.class.getDeclaredField("modifiers"); ++ modifiersField.setAccessible(true); ++ modifiersField.setInt(field, field.getModifiers() & 0xFFFFFFEF); ++ Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, new Object[] { field, Boolean.valueOf(false) }); ++ fieldAccessorSet.invoke(fieldAccessor, new Object[] { target, value }); ++ } ++ ++ private static void blankField(Class enumClass, String fieldName) throws Exception { ++ for (Field field : Class.class.getDeclaredFields()) ++ if (field.getName().contains(fieldName)) { ++ field.setAccessible(true); ++ setFailsafeFieldValue(field, enumClass, null); ++ break; ++ } ++ } ++ ++ private static void cleanEnumCache(Class enumClass) throws Exception ++ { ++ blankField(enumClass, "enumConstantDirectory"); ++ blankField(enumClass, "enumConstants"); ++ } ++ ++ public static > T replaceEnum(Class enumType, String enumName, int ordinal, Class[] paramTypes, Object[] paramValues) ++ { ++ if (!isSetup) setup(); ++ Field valuesField = null; ++ Field[] fields = enumType.getDeclaredFields(); ++ int flags = 4122; ++ String valueType = String.format("[L%s;", new Object[] { enumType.getName() }); ++ ++ for (Field field : fields) { ++ if (((field.getModifiers() & flags) != flags) || (!field.getType().getName().equals(valueType))) { ++ continue; ++ } ++ valuesField = field; ++ break; ++ } ++ ++ valuesField.setAccessible(true); ++ try ++ { ++ Enum[] previousValues = (Enum[])(Enum[])valuesField.get(enumType); ++ Enum[] newValues = new Enum[previousValues.length]; ++ Enum newValue = null; ++ for (Enum enumValue : previousValues) ++ { ++ if (enumValue.ordinal() == ordinal) ++ { ++ newValue = makeEnum(enumType, enumName, ordinal, paramTypes, paramValues); ++ newValues[enumValue.ordinal()] = newValue; ++ } ++ else newValues[enumValue.ordinal()] = enumValue; ++ } ++ List values = new ArrayList(Arrays.asList(newValues)); ++ ++ setFailsafeFieldValue(valuesField, null, values.toArray((Enum[])(Enum[])Array.newInstance(enumType, 0))); ++ cleanEnumCache(enumType); ++ return (T) newValue; ++ } catch (Exception e) { ++ e.printStackTrace(); ++ throw new RuntimeException(e.getMessage(), e); ++ } ++ } ++ /* =============================== Cauldron END============================= */ ++ + static { ++ // Cauldron start ++ byId = new Material[32000]; ++ BY_NAME = Maps.newHashMap(); ++ ++ reflectionFactory = null; ++ newConstructorAccessor = null; ++ newInstance = null; ++ newFieldAccessor = null; ++ fieldAccessorSet = null; ++ isSetup = false; ++ // Cauldron end + for (Material material : values()) { + if (byId.length > material.id) { + byId[material.id] = material; diff --git a/patches/org/bukkit/World.java.patch b/patches/org/bukkit/World.java.patch new file mode 100644 index 0000000..86e15f0 --- /dev/null +++ b/patches/org/bukkit/World.java.patch @@ -0,0 +1,72 @@ +--- ../src-base/minecraft/org/bukkit/World.java ++++ ../src-work/minecraft/org/bukkit/World.java +@@ -1155,6 +1155,56 @@ + */ + public boolean isGameRule(String rule); + ++ // Spigot start ++ public class Spigot ++ { ++ ++ /** ++ * Plays an effect to all players within a default radius around a given ++ * location. ++ * ++ * @param location the {@link Location} around which players must be to ++ * see the effect ++ * @param effect the {@link Effect} ++ * @throws IllegalArgumentException if the location or effect is null. ++ * It also throws when the effect requires a material or a material data ++ */ ++ public void playEffect(Location location, Effect effect) ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ ++ /** ++ * Plays an effect to all players within a default radius around a given ++ * location. The effect will use the provided material (and material ++ * data if required). The particle's position on the client will be the ++ * given location, adjusted on each axis by a normal distribution with ++ * mean 0 and standard deviation given in the offset parameters, each ++ * particle has independently calculated offsets. The effect will have ++ * the given speed and particle count if the effect is a particle. Some ++ * effect will create multiple particles. ++ * ++ * @param location the {@link Location} around which players must be to ++ * see the effect ++ * @param effect effect the {@link Effect} ++ * @param id the item/block/data id for the effect ++ * @param data the data value of the block/item for the effect ++ * @param offsetX the amount to be randomly offset by in the X axis ++ * @param offsetY the amount to be randomly offset by in the Y axis ++ * @param offsetZ the amount to be randomly offset by in the Z axis ++ * @param speed the speed of the particles ++ * @param particleCount the number of particles ++ * @param radius the radius around the location ++ */ ++ public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ } ++ ++ Spigot spigot(); ++ // Spigot end ++ + /** + * Represents various map environment types that a world may be + */ +@@ -1203,6 +1253,12 @@ + return lookup.get(id); + } + ++ // Cauldron start - allow forge to register environments ++ public static void registerEnvironment(Environment env) { ++ lookup.put(env.getId(),env); ++ } ++ // Cauldron end ++ + static { + for (Environment env : values()) { + lookup.put(env.getId(), env); diff --git a/patches/org/bukkit/command/Command.java.patch b/patches/org/bukkit/command/Command.java.patch new file mode 100644 index 0000000..b82033f --- /dev/null +++ b/patches/org/bukkit/command/Command.java.patch @@ -0,0 +1,26 @@ +--- ../src-base/minecraft/org/bukkit/command/Command.java ++++ ../src-work/minecraft/org/bukkit/command/Command.java +@@ -31,6 +31,7 @@ + protected String usageMessage; + private String permission; + private String permissionMessage; ++ public org.spigotmc.CustomTimingsHandler timings; // Spigot + + protected Command(String name) { + this(name, "", "/" + name, new ArrayList()); +@@ -44,6 +45,7 @@ + this.usageMessage = usageMessage; + this.aliases = aliases; + this.activeAliases = new ArrayList(aliases); ++ this.timings = new org.spigotmc.CustomTimingsHandler("** Command: " + name); // Spigot + } + + /** +@@ -200,6 +202,7 @@ + public boolean setLabel(String name) { + this.nextLabel = name; + if (!isRegistered()) { ++ this.timings = new org.spigotmc.CustomTimingsHandler("** Command: " + name); // Spigot + this.label = name; + return true; + } diff --git a/patches/org/bukkit/command/SimpleCommandMap.java.patch b/patches/org/bukkit/command/SimpleCommandMap.java.patch new file mode 100644 index 0000000..7dcd0fa --- /dev/null +++ b/patches/org/bukkit/command/SimpleCommandMap.java.patch @@ -0,0 +1,18 @@ +--- ../src-base/minecraft/org/bukkit/command/SimpleCommandMap.java ++++ ../src-work/minecraft/org/bukkit/command/SimpleCommandMap.java +@@ -176,11 +176,15 @@ + } + + try { ++ target.timings.startTiming(); // Spigot + // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) + target.execute(sender, sentCommandLabel, Arrays_copyOfRange(args, 1, args.length)); ++ target.timings.stopTiming(); // Spigot + } catch (CommandException ex) { ++ target.timings.stopTiming(); // Spigot + throw ex; + } catch (Throwable ex) { ++ target.timings.stopTiming(); // Spigot + throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex); + } + diff --git a/patches/org/bukkit/command/defaults/GameModeCommand.java.patch b/patches/org/bukkit/command/defaults/GameModeCommand.java.patch new file mode 100644 index 0000000..b0d0308 --- /dev/null +++ b/patches/org/bukkit/command/defaults/GameModeCommand.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/org/bukkit/command/defaults/GameModeCommand.java ++++ ../src-work/minecraft/org/bukkit/command/defaults/GameModeCommand.java +@@ -50,7 +50,7 @@ + + GameMode mode = GameMode.getByValue(value); + +- if (mode == null) { ++ if (mode == null || mode == GameMode.NOT_SET) { // Cauldron + if (modeArg.equalsIgnoreCase("creative") || modeArg.equalsIgnoreCase("c")) { + mode = GameMode.CREATIVE; + } else if (modeArg.equalsIgnoreCase("adventure") || modeArg.equalsIgnoreCase("a")) { diff --git a/patches/org/bukkit/command/defaults/PluginsCommand.java.patch b/patches/org/bukkit/command/defaults/PluginsCommand.java.patch new file mode 100644 index 0000000..6979979 --- /dev/null +++ b/patches/org/bukkit/command/defaults/PluginsCommand.java.patch @@ -0,0 +1,15 @@ +--- ../src-base/minecraft/org/bukkit/command/defaults/PluginsCommand.java ++++ ../src-work/minecraft/org/bukkit/command/defaults/PluginsCommand.java +@@ -40,4 +40,12 @@ + + return "(" + plugins.length + "): " + pluginList.toString(); + } ++ ++ // Spigot Start ++ @Override ++ public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException ++ { ++ return java.util.Collections.emptyList(); ++ } ++ // Spigot End + } diff --git a/patches/org/bukkit/command/defaults/ReloadCommand.java.patch b/patches/org/bukkit/command/defaults/ReloadCommand.java.patch new file mode 100644 index 0000000..35a890b --- /dev/null +++ b/patches/org/bukkit/command/defaults/ReloadCommand.java.patch @@ -0,0 +1,28 @@ +--- ../src-base/minecraft/org/bukkit/command/defaults/ReloadCommand.java ++++ ../src-work/minecraft/org/bukkit/command/defaults/ReloadCommand.java +@@ -18,11 +18,25 @@ + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { ++ // Cauldron start - disable reload as it causes many issues with mods ++ /* + if (!testPermission(sender)) return true; + ++ org.spigotmc.CustomTimingsHandler.reload(); // Spigot: TODO: Why is this here? + Bukkit.reload(); + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); ++ */ ++ sender.sendMessage(ChatColor.RED + "Reload not allowed on a Cauldron server."); ++ // Cauldron end + + return true; + } ++ ++ // Spigot Start ++ @Override ++ public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException ++ { ++ return java.util.Collections.emptyList(); ++ } ++ // Spigot End + } diff --git a/patches/org/bukkit/command/defaults/TellCommand.java.patch b/patches/org/bukkit/command/defaults/TellCommand.java.patch new file mode 100644 index 0000000..3214f12 --- /dev/null +++ b/patches/org/bukkit/command/defaults/TellCommand.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/org/bukkit/command/defaults/TellCommand.java ++++ ../src-work/minecraft/org/bukkit/command/defaults/TellCommand.java +@@ -45,4 +45,16 @@ + + return true; + } ++ ++ // Spigot Start ++ @Override ++ public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException ++ { ++ if ( args.length == 0 ) ++ { ++ return super.tabComplete( sender, alias, args ); ++ } ++ return java.util.Collections.emptyList(); ++ } ++ // Spigot End + } diff --git a/patches/org/bukkit/command/defaults/TestForCommand.java.patch b/patches/org/bukkit/command/defaults/TestForCommand.java.patch new file mode 100644 index 0000000..8255145 --- /dev/null +++ b/patches/org/bukkit/command/defaults/TestForCommand.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/org/bukkit/command/defaults/TestForCommand.java ++++ ../src-work/minecraft/org/bukkit/command/defaults/TestForCommand.java +@@ -23,4 +23,16 @@ + sender.sendMessage(ChatColor.RED + "/testfor is only usable by commandblocks with analog output."); + return true; + } ++ ++ // Spigot Start ++ @Override ++ public java.util.List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException ++ { ++ if ( args.length == 0 ) ++ { ++ return super.tabComplete( sender, alias, args ); ++ } ++ return java.util.Collections.emptyList(); ++ } ++ // Spigot End + } diff --git a/patches/org/bukkit/command/defaults/TimingsCommand.java.patch b/patches/org/bukkit/command/defaults/TimingsCommand.java.patch new file mode 100644 index 0000000..6ce3d2c --- /dev/null +++ b/patches/org/bukkit/command/defaults/TimingsCommand.java.patch @@ -0,0 +1,160 @@ +--- ../src-base/minecraft/org/bukkit/command/defaults/TimingsCommand.java ++++ ../src-work/minecraft/org/bukkit/command/defaults/TimingsCommand.java +@@ -19,23 +19,97 @@ + + import com.google.common.collect.ImmutableList; + ++// Spigot start ++import java.io.ByteArrayOutputStream; ++import java.io.OutputStream; ++import java.net.HttpURLConnection; ++import java.net.URL; ++import java.net.URLEncoder; ++import java.util.logging.Level; ++ ++import org.bukkit.command.RemoteConsoleCommandSender; ++import org.bukkit.plugin.SimplePluginManager; ++import org.spigotmc.CustomTimingsHandler; ++// Spigot end ++ + public class TimingsCommand extends BukkitCommand { +- private static final List TIMINGS_SUBCOMMANDS = ImmutableList.of("merged", "reset", "separate"); ++ private static final List TIMINGS_SUBCOMMANDS = ImmutableList.of("report", "reset", "on", "off", "paste"); // Spigot ++ public static long timingStart = 0; // Spigot + + public TimingsCommand(String name) { + super(name); +- this.description = "Records timings for all plugin events"; +- this.usageMessage = "/timings "; ++ this.description = "Manages Spigot Timings data to see performance of the server."; // Spigot ++ this.usageMessage = "/timings "; // Spigot + this.setPermission("bukkit.command.timings"); + } + ++ // Spigot start - redesigned Timings Command ++ public void executeSpigotTimings(CommandSender sender, String[] args) { ++ if ( "on".equals( args[0] ) ) ++ { ++ ( (SimplePluginManager) Bukkit.getPluginManager() ).useTimings( true ); ++ CustomTimingsHandler.reload(); ++ sender.sendMessage( "Enabled Timings & Reset" ); ++ return; ++ } else if ( "off".equals( args[0] ) ) ++ { ++ ( (SimplePluginManager) Bukkit.getPluginManager() ).useTimings( false ); ++ sender.sendMessage( "Disabled Timings" ); ++ return; ++ } ++ ++ if ( !Bukkit.getPluginManager().useTimings() ) ++ { ++ sender.sendMessage( "Please enable timings by typing /timings on" ); ++ return; ++ } ++ ++ boolean paste = "paste".equals( args[0] ); ++ if ("reset".equals(args[0])) { ++ CustomTimingsHandler.reload(); ++ sender.sendMessage("Timings reset"); ++ } else if ("merged".equals(args[0]) || "report".equals(args[0]) || paste) { ++ long sampleTime = System.nanoTime() - timingStart; ++ int index = 0; ++ File timingFolder = new File("timings"); ++ timingFolder.mkdirs(); ++ File timings = new File(timingFolder, "timings.txt"); ++ ByteArrayOutputStream bout = ( paste ) ? new ByteArrayOutputStream() : null; ++ while (timings.exists()) timings = new File(timingFolder, "timings" + (++index) + ".txt"); ++ PrintStream fileTimings = null; ++ try { ++ fileTimings = ( paste ) ? new PrintStream( bout ) : new PrintStream( timings ); ++ ++ CustomTimingsHandler.printTimings(fileTimings); ++ fileTimings.println( "Sample time " + sampleTime + " (" + sampleTime / 1E9 + "s)" ); ++ ++ if ( paste ) ++ { ++ new PasteThread( sender, bout ).start(); ++ return; ++ } ++ ++ sender.sendMessage("Timings written to " + timings.getPath()); ++ sender.sendMessage( "Paste contents of file into form at http://www.spigotmc.org/go/timings to read results." ); ++ ++ } catch (IOException e) { ++ } finally { ++ if (fileTimings != null) { ++ fileTimings.close(); ++ } ++ } ++ } ++ } ++ // Spigot end ++ + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) { + if (!testPermission(sender)) return true; +- if (args.length != 1) { ++ if (args.length < 1) { // Spigot + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } ++ if (true) { executeSpigotTimings(sender, args); return true; } // Spigot + if (!sender.getServer().getPluginManager().useTimings()) { + sender.sendMessage("Please enable timings by setting \"settings.plugin-profiling\" to true in bukkit.yml"); + return true; +@@ -118,4 +192,55 @@ + } + return ImmutableList.of(); + } ++ ++ // Spigot start ++ private static class PasteThread extends Thread ++ { ++ ++ private final CommandSender sender; ++ private final ByteArrayOutputStream bout; ++ ++ public PasteThread(CommandSender sender, ByteArrayOutputStream bout) ++ { ++ super( "Timings paste thread" ); ++ this.sender = sender; ++ this.bout = bout; ++ } ++ ++ @Override ++ public synchronized void start() { ++ if (sender instanceof RemoteConsoleCommandSender) { ++ run(); ++ } else { ++ super.start(); ++ } ++ } ++ ++ @Override ++ public void run() ++ { ++ try ++ { ++ HttpURLConnection con = (HttpURLConnection) new URL( "http://paste.ubuntu.com/" ).openConnection(); ++ con.setDoOutput( true ); ++ con.setRequestMethod( "POST" ); ++ con.setInstanceFollowRedirects( false ); ++ ++ OutputStream out = con.getOutputStream(); ++ out.write( "poster=Spigot&syntax=text&content=".getBytes( "UTF-8" ) ); ++ out.write( URLEncoder.encode( bout.toString( "UTF-8" ), "UTF-8" ).getBytes( "UTF-8" ) ); ++ out.close(); ++ con.getInputStream().close(); ++ ++ String location = con.getHeaderField( "Location" ); ++ String pasteID = location.substring( "http://paste.ubuntu.com/".length(), location.length() - 1 ); ++ sender.sendMessage( ChatColor.GREEN + "View timings results can be viewed at http://www.spigotmc.org/go/timings?url=" + pasteID ); ++ } catch ( IOException ex ) ++ { ++ sender.sendMessage( ChatColor.RED + "Error pasting timings, check your console for more information" ); ++ Bukkit.getServer().getLogger().log( Level.WARNING, "Could not paste timings", ex ); ++ } ++ } ++ } ++ // Spigot end + } diff --git a/patches/org/bukkit/conversations/BooleanPrompt.java.patch b/patches/org/bukkit/conversations/BooleanPrompt.java.patch new file mode 100644 index 0000000..5e6a84b --- /dev/null +++ b/patches/org/bukkit/conversations/BooleanPrompt.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/org/bukkit/conversations/BooleanPrompt.java ++++ ../src-work/minecraft/org/bukkit/conversations/BooleanPrompt.java +@@ -15,12 +15,13 @@ + + @Override + protected boolean isInputValid(ConversationContext context, String input) { +- String[] accepted = {"true", "false", "on", "off", "yes", "no"}; ++ String[] accepted = {"true", "false", "on", "off", "yes", "no" /* Spigot: */, "y", "n", "1", "0", "right", "wrong", "correct", "incorrect", "valid", "invalid"}; // Spigot + return ArrayUtils.contains(accepted, input.toLowerCase()); + } + + @Override + protected Prompt acceptValidatedInput(ConversationContext context, String input) { ++ if (input.equalsIgnoreCase("y") || input.equals("1") || input.equalsIgnoreCase("right") || input.equalsIgnoreCase("correct") || input.equalsIgnoreCase("valid")) input = "true"; // Spigot + return acceptValidatedInput(context, BooleanUtils.toBoolean(input)); + } + diff --git a/patches/org/bukkit/conversations/Conversation.java.patch b/patches/org/bukkit/conversations/Conversation.java.patch new file mode 100644 index 0000000..7fe8cb7 --- /dev/null +++ b/patches/org/bukkit/conversations/Conversation.java.patch @@ -0,0 +1,23 @@ +--- ../src-base/minecraft/org/bukkit/conversations/Conversation.java ++++ ../src-work/minecraft/org/bukkit/conversations/Conversation.java +@@ -209,6 +209,7 @@ + * @param input The user's chat text. + */ + public void acceptInput(String input) { ++ try { // Spigot + if (currentPrompt != null) { + + // Echo the user's input +@@ -228,6 +229,12 @@ + currentPrompt = currentPrompt.acceptInput(context, input); + outputNextPrompt(); + } ++ // Spigot Start ++ } catch ( Throwable t ) ++ { ++ org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.SEVERE, "Error handling conversation prompt", t ); ++ } ++ // Spigot End + } + + /** diff --git a/patches/org/bukkit/entity/Arrow.java.patch b/patches/org/bukkit/entity/Arrow.java.patch new file mode 100644 index 0000000..ac85d36 --- /dev/null +++ b/patches/org/bukkit/entity/Arrow.java.patch @@ -0,0 +1,25 @@ +--- ../src-base/minecraft/org/bukkit/entity/Arrow.java ++++ ../src-work/minecraft/org/bukkit/entity/Arrow.java +@@ -39,4 +39,22 @@ + * @param critical whether or not it should be critical + */ + public void setCritical(boolean critical); ++ ++ // Spigot start ++ public class Spigot extends Entity.Spigot ++ { ++ ++ public double getDamage() ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ ++ public void setDamage(double damage) ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ } ++ ++ Spigot spigot(); ++ // Spigot end + } diff --git a/patches/org/bukkit/entity/Entity.java.patch b/patches/org/bukkit/entity/Entity.java.patch new file mode 100644 index 0000000..8c53505 --- /dev/null +++ b/patches/org/bukkit/entity/Entity.java.patch @@ -0,0 +1,25 @@ +--- ../src-base/minecraft/org/bukkit/entity/Entity.java ++++ ../src-work/minecraft/org/bukkit/entity/Entity.java +@@ -291,4 +291,22 @@ + * @return The current vehicle. + */ + public Entity getVehicle(); ++ ++ // Spigot Start ++ public class Spigot ++ { ++ ++ /** ++ * Returns whether this entity is invulnerable. ++ * ++ * @return True if the entity is invulnerable. ++ */ ++ public boolean isInvulnerable() ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ } ++ ++ Spigot spigot(); ++ // Spigot End + } diff --git a/patches/org/bukkit/entity/Player.java.patch b/patches/org/bukkit/entity/Player.java.patch new file mode 100644 index 0000000..81938be --- /dev/null +++ b/patches/org/bukkit/entity/Player.java.patch @@ -0,0 +1,80 @@ +--- ../src-base/minecraft/org/bukkit/entity/Player.java ++++ ../src-work/minecraft/org/bukkit/entity/Player.java +@@ -1035,4 +1035,77 @@ + * @see Player#setHealthScaled(boolean) + */ + public double getHealthScale(); ++ ++ // Spigot start ++ public class Spigot extends Entity.Spigot ++ { ++ ++ /** ++ * Gets the connection address of this player, regardless of whether it ++ * has been spoofed or not. ++ * ++ * @return the player's connection address ++ */ ++ public InetSocketAddress getRawAddress() ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ ++ public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ ++ /** ++ * Gets whether the player collides with entities ++ * ++ * @return the player's collision toggle state ++ */ ++ public boolean getCollidesWithEntities() ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ ++ /** ++ * Sets whether the player collides with entities ++ * ++ * @param collides whether the player should collide with entities or ++ * not. ++ */ ++ public void setCollidesWithEntities(boolean collides) ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ ++ /** ++ * Respawns the player if dead. ++ */ ++ public void respawn() ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ ++ /** ++ * Gets player locale language. ++ * ++ * @return the player's client language settings ++ */ ++ public String getLocale() ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ ++ /** ++ * Gets all players hidden with {@link hidePlayer(org.bukkit.entity.Player)}. ++ * ++ * @return a Set with all hidden players ++ */ ++ public java.util.Set getHiddenPlayers() ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ } ++ ++ Spigot spigot(); ++ // Spigot end + } diff --git a/patches/org/bukkit/event/entity/EntityDamageByBlockEvent.java.patch b/patches/org/bukkit/event/entity/EntityDamageByBlockEvent.java.patch new file mode 100644 index 0000000..73587af --- /dev/null +++ b/patches/org/bukkit/event/entity/EntityDamageByBlockEvent.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/org/bukkit/event/entity/EntityDamageByBlockEvent.java ++++ ../src-work/minecraft/org/bukkit/event/entity/EntityDamageByBlockEvent.java +@@ -2,6 +2,7 @@ + + import java.util.Map; + ++import com.google.common.base.Function; + import org.bukkit.block.Block; + import org.bukkit.entity.Entity; + +@@ -22,8 +23,8 @@ + this.damager = damager; + } + +- public EntityDamageByBlockEvent(final Block damager, final Entity damagee, final DamageCause cause, final Map modifiers) { +- super(damagee, cause, modifiers); ++ public EntityDamageByBlockEvent(final Block damager, final Entity damagee, final DamageCause cause, final Map modifiers, final Map> modifierFunctions) { ++ super(damagee, cause, modifiers, modifierFunctions); + this.damager = damager; + } + diff --git a/patches/org/bukkit/event/entity/EntityDamageByEntityEvent.java.patch b/patches/org/bukkit/event/entity/EntityDamageByEntityEvent.java.patch new file mode 100644 index 0000000..a877dd8 --- /dev/null +++ b/patches/org/bukkit/event/entity/EntityDamageByEntityEvent.java.patch @@ -0,0 +1,21 @@ +--- ../src-base/minecraft/org/bukkit/event/entity/EntityDamageByEntityEvent.java ++++ ../src-work/minecraft/org/bukkit/event/entity/EntityDamageByEntityEvent.java +@@ -2,6 +2,7 @@ + + import java.util.Map; + ++import com.google.common.base.Function; + import org.bukkit.entity.Entity; + + /** +@@ -21,8 +22,8 @@ + this.damager = damager; + } + +- public EntityDamageByEntityEvent(final Entity damager, final Entity damagee, final DamageCause cause, final Map modifiers) { +- super(damagee, cause, modifiers); ++ public EntityDamageByEntityEvent(final Entity damager, final Entity damagee, final DamageCause cause, final Map modifiers, final Map> modifierFunctions) { ++ super(damagee, cause, modifiers, modifierFunctions); + this.damager = damager; + } + diff --git a/patches/org/bukkit/event/entity/EntityDamageEvent.java.patch b/patches/org/bukkit/event/entity/EntityDamageEvent.java.patch new file mode 100644 index 0000000..60d0cb0 --- /dev/null +++ b/patches/org/bukkit/event/entity/EntityDamageEvent.java.patch @@ -0,0 +1,85 @@ +--- ../src-base/minecraft/org/bukkit/event/entity/EntityDamageEvent.java ++++ ../src-work/minecraft/org/bukkit/event/entity/EntityDamageEvent.java +@@ -10,6 +10,8 @@ + import org.bukkit.event.HandlerList; + import org.bukkit.util.NumberConversions; + ++import com.google.common.base.Function; ++import com.google.common.base.Functions; + import com.google.common.collect.ImmutableMap; + + /** +@@ -18,7 +20,9 @@ + public class EntityDamageEvent extends EntityEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + private static final DamageModifier[] MODIFIERS = DamageModifier.values(); ++ private static final Function ZERO = Functions.constant(-0.0); + private final Map modifiers; ++ private final Map> modifierFunctions; + private final Map originals; + private boolean cancelled; + private final DamageCause cause; +@@ -30,16 +34,20 @@ + + @Deprecated + public EntityDamageEvent(final Entity damagee, final DamageCause cause, final double damage) { +- this(damagee, cause, new EnumMap(ImmutableMap.of(DamageModifier.BASE, damage))); ++ this(damagee, cause, new EnumMap(ImmutableMap.of(DamageModifier.BASE, damage)), new EnumMap>(ImmutableMap.of(DamageModifier.BASE, ZERO))); + } + +- public EntityDamageEvent(final Entity damagee, final DamageCause cause, final Map modifiers) { ++ public EntityDamageEvent(final Entity damagee, final DamageCause cause, final Map modifiers, final Map> modifierFunctions) { + super(damagee); + Validate.isTrue(modifiers.containsKey(DamageModifier.BASE), "BASE DamageModifier missing"); + Validate.isTrue(!modifiers.containsKey(null), "Cannot have null DamageModifier"); ++ Validate.noNullElements(modifiers.values(), "Cannot have null modifier values"); ++ Validate.isTrue(modifiers.keySet().equals(modifierFunctions.keySet()), "Must have a modifier function for each DamageModifier"); ++ Validate.noNullElements(modifierFunctions.values(), "Cannot have null modifier function"); + this.originals = new EnumMap(modifiers); + this.cause = cause; + this.modifiers = modifiers; ++ this.modifierFunctions = modifierFunctions; + } + + public boolean isCancelled() { +@@ -149,11 +157,39 @@ + } + + /** +- * Sets the raw amount of damage caused by the event ++ * Sets the raw amount of damage caused by the event. ++ *

++ * For compatibility this also recalculates the modifiers and scales ++ * them by the difference between the modifier for the previous damage ++ * value and the new one. + * + * @param damage The raw amount of damage caused by the event + */ + public void setDamage(double damage) { ++ // These have to happen in the same order as the server calculates them, keep the enum sorted ++ double remaining = damage; ++ double oldRemaining = getDamage(DamageModifier.BASE); ++ for (DamageModifier modifier : MODIFIERS) { ++ if (!isApplicable(modifier)) { ++ continue; ++ } ++ ++ Function modifierFunction = modifierFunctions.get(modifier); ++ double newVanilla = modifierFunction.apply(remaining); ++ double oldVanilla = modifierFunction.apply(oldRemaining); ++ double difference = oldVanilla - newVanilla; ++ ++ // Don't allow value to cross zero, assume zero values should be negative ++ double old = getDamage(modifier); ++ if (old > 0) { ++ setDamage(modifier, Math.max(0, old - difference)); ++ } else { ++ setDamage(modifier, Math.min(0, old - difference)); ++ } ++ remaining += newVanilla; ++ oldRemaining += oldVanilla; ++ } ++ + setDamage(DamageModifier.BASE, damage); + } + diff --git a/patches/org/bukkit/event/player/PlayerLoginEvent.java.patch b/patches/org/bukkit/event/player/PlayerLoginEvent.java.patch new file mode 100644 index 0000000..45e002a --- /dev/null +++ b/patches/org/bukkit/event/player/PlayerLoginEvent.java.patch @@ -0,0 +1,66 @@ +--- ../src-base/minecraft/org/bukkit/event/player/PlayerLoginEvent.java ++++ ../src-work/minecraft/org/bukkit/event/player/PlayerLoginEvent.java +@@ -14,6 +14,7 @@ + private final String hostname; + private Result result = Result.ALLOWED; + private String message = ""; ++ private final InetAddress realAddress; // Spigot + + /** + * @deprecated Address should be provided in other constructor +@@ -40,19 +41,26 @@ + * @param address The address the player used to connect, provided for + * timing issues + */ +- public PlayerLoginEvent(final Player player, final String hostname, final InetAddress address) { ++ public PlayerLoginEvent(final Player player, final String hostname, final InetAddress address, final InetAddress realAddress) { // Spigot + super(player); + this.hostname = hostname; + this.address = address; ++ // Spigot start ++ this.realAddress = address; + } + ++ public PlayerLoginEvent(final Player player, final String hostname, final InetAddress address) { ++ this(player, hostname, address, address); ++ // Spigot end ++ } ++ + /** + * @deprecated Address and hostname should be provided in other + * constructor + */ + @Deprecated + public PlayerLoginEvent(final Player player, final Result result, final String message) { +- this(player, "", null, result, message); ++ this(player, "", null, result, message, null); // Spigot + } + + /** +@@ -65,13 +73,24 @@ + * @param result The result status for this event + * @param message The message to be displayed if result denies login + */ +- public PlayerLoginEvent(final Player player, String hostname, final InetAddress address, final Result result, final String message) { +- this(player, hostname, address); ++ public PlayerLoginEvent(final Player player, String hostname, final InetAddress address, final Result result, final String message, final InetAddress realAddress) { // Spigot ++ this(player, hostname, address, realAddress); // Spigot + this.result = result; + this.message = message; + } + ++ // Spigot start + /** ++ * Gets the connection address of this player, regardless of whether it has been spoofed or not. ++ * ++ * @return the player's connection address ++ */ ++ public InetAddress getRealAddress() { ++ return realAddress; ++ } ++ // Spigot end ++ ++ /** + * Gets the current result of the login, as an enum + * + * @return Current Result of the login diff --git a/patches/org/bukkit/event/player/PlayerTeleportEvent.java.patch b/patches/org/bukkit/event/player/PlayerTeleportEvent.java.patch new file mode 100644 index 0000000..be76ae5 --- /dev/null +++ b/patches/org/bukkit/event/player/PlayerTeleportEvent.java.patch @@ -0,0 +1,17 @@ +--- ../src-base/minecraft/org/bukkit/event/player/PlayerTeleportEvent.java ++++ ../src-work/minecraft/org/bukkit/event/player/PlayerTeleportEvent.java +@@ -55,7 +55,14 @@ + * portal + */ + END_PORTAL, ++ // Cauldron start - added cause for mods + /** ++ * Indicates the teleportation was caused by a player entering a ++ * Mod portal ++ */ ++ MOD, ++ // Cauldron end ++ /** + * Indicates the teleportation was caused by an event not covered by + * this enum + */ diff --git a/patches/org/bukkit/plugin/SimplePluginManager.java.patch b/patches/org/bukkit/plugin/SimplePluginManager.java.patch new file mode 100644 index 0000000..e64bc92 --- /dev/null +++ b/patches/org/bukkit/plugin/SimplePluginManager.java.patch @@ -0,0 +1,31 @@ +--- ../src-base/minecraft/org/bukkit/plugin/SimplePluginManager.java ++++ ../src-work/minecraft/org/bukkit/plugin/SimplePluginManager.java +@@ -132,7 +132,9 @@ + try { + description = loader.getPluginDescription(file); + String name = description.getName(); +- if (name.equalsIgnoreCase("bukkit") || name.equalsIgnoreCase("minecraft") || name.equalsIgnoreCase("mojang")) { ++ if (name.equalsIgnoreCase("bukkit") || name.equalsIgnoreCase("minecraft") || name.equalsIgnoreCase("mojang") ++ // Cauldron - Add more restricted names ++ || name.equalsIgnoreCase("spigot") || name.equalsIgnoreCase("forge") || name.equalsIgnoreCase("cauldron") || name.equalsIgnoreCase("mcpc")) { + server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': Restricted Name"); + continue; + } else if (description.rawName.indexOf(' ') != -1) { +@@ -188,6 +190,9 @@ + } + } + ++ // Cauldron - fill names for Cauldron-provided dependencies ++ loadedPlugins.addAll(ImmutableSet.of("Cauldron", "Forge", "MCPC", "MCPC+")); ++ + while (!plugins.isEmpty()) { + boolean missingDependency = true; + Iterator pluginIterator = plugins.keySet().iterator(); +@@ -295,6 +300,7 @@ + } + } + ++ org.bukkit.command.defaults.TimingsCommand.timingStart = System.nanoTime(); // Spigot + return result.toArray(new Plugin[result.size()]); + } + diff --git a/patches/org/bukkit/plugin/SimpleServicesManager.java.patch b/patches/org/bukkit/plugin/SimpleServicesManager.java.patch new file mode 100644 index 0000000..0c5733c --- /dev/null +++ b/patches/org/bukkit/plugin/SimpleServicesManager.java.patch @@ -0,0 +1,12 @@ +--- ../src-base/minecraft/org/bukkit/plugin/SimpleServicesManager.java ++++ ../src-work/minecraft/org/bukkit/plugin/SimpleServicesManager.java +@@ -79,7 +79,8 @@ + while (it2.hasNext()) { + RegisteredServiceProvider registered = it2.next(); + +- if (registered.getPlugin().equals(plugin)) { ++ Plugin oPlugin = registered.getPlugin(); ++ if (oPlugin != null ? oPlugin.equals(plugin) : plugin == null) { + it2.remove(); + unregisteredEvents.add(new ServiceUnregisterEvent(registered)); + } diff --git a/patches/org/bukkit/plugin/TimedRegisteredListener.java.patch b/patches/org/bukkit/plugin/TimedRegisteredListener.java.patch new file mode 100644 index 0000000..7d0a6c8 --- /dev/null +++ b/patches/org/bukkit/plugin/TimedRegisteredListener.java.patch @@ -0,0 +1,51 @@ +--- ../src-base/minecraft/org/bukkit/plugin/TimedRegisteredListener.java ++++ ../src-work/minecraft/org/bukkit/plugin/TimedRegisteredListener.java +@@ -11,6 +11,10 @@ + public class TimedRegisteredListener extends RegisteredListener { + private int count; + private long totalTime; ++ // Spigot start ++ public long curTickTotal = 0; ++ public long violations = 0; ++ // Spigot end + private Class eventClass; + private boolean multiple = false; + +@@ -20,6 +24,13 @@ + + @Override + public void callEvent(Event event) throws EventException { ++ // Spigot start ++ if ( org.bukkit.Bukkit.getServer() != null && !org.bukkit.Bukkit.getServer().getPluginManager().useTimings() ) ++ { ++ super.callEvent( event ); ++ return; ++ } ++ // Spigot end + if (event.isAsynchronous()) { + super.callEvent(event); + return; +@@ -34,7 +45,11 @@ + } + long start = System.nanoTime(); + super.callEvent(event); +- totalTime += System.nanoTime() - start; ++ // Spigot start ++ long diff = System.nanoTime() - start; ++ curTickTotal += diff; ++ totalTime += diff; ++ // Spigot end + } + + private static Class getCommonSuperclass(Class class1, Class class2) { +@@ -50,6 +65,10 @@ + public void reset() { + count = 0; + totalTime = 0; ++ // Spigot start ++ curTickTotal = 0; ++ violations = 0; ++ // Spigot end + } + + /** diff --git a/patches/org/bukkit/plugin/java/JavaPluginLoader.java.patch b/patches/org/bukkit/plugin/java/JavaPluginLoader.java.patch new file mode 100644 index 0000000..6acf7fe --- /dev/null +++ b/patches/org/bukkit/plugin/java/JavaPluginLoader.java.patch @@ -0,0 +1,233 @@ +--- ../src-base/minecraft/org/bukkit/plugin/java/JavaPluginLoader.java ++++ ../src-work/minecraft/org/bukkit/plugin/java/JavaPluginLoader.java +@@ -1,5 +1,15 @@ + package org.bukkit.plugin.java; + ++// Cauldron start ++import java.io.BufferedReader; ++import java.io.InputStreamReader; ++import com.google.common.collect.BiMap; ++import com.google.common.collect.HashBiMap; ++import net.md_5.specialsource.InheritanceMap; ++import net.md_5.specialsource.JarMapping; ++import net.md_5.specialsource.transformer.MavenShade; ++// Cauldron end ++ + import java.io.File; + import java.io.FileNotFoundException; + import java.io.IOException; +@@ -10,6 +20,7 @@ + import java.util.HashMap; + import java.util.HashSet; + import java.util.LinkedHashMap; ++import java.util.List; + import java.util.Map; + import java.util.Set; + import java.util.jar.JarEntry; +@@ -39,8 +50,11 @@ + import org.bukkit.plugin.RegisteredListener; + import org.bukkit.plugin.TimedRegisteredListener; + import org.bukkit.plugin.UnknownDependencyException; ++import org.spigotmc.CustomTimingsHandler; // Spigot + import org.yaml.snakeyaml.error.YAMLException; + ++import com.google.common.collect.ImmutableList; ++ + /** + * Represents a Java plugin loader, allowing plugins in the form of .jar + */ +@@ -49,6 +63,7 @@ + private final Pattern[] fileFilters = new Pattern[] { Pattern.compile("\\.jar$"), }; + private final Map> classes = new HashMap>(); + private final Map loaders = new LinkedHashMap(); ++ public static final CustomTimingsHandler pluginParentTimer = new CustomTimingsHandler("** Plugins"); // Spigot + + /** + * This class was not meant to be constructed explicitly +@@ -59,43 +74,41 @@ + server = instance; + } + +- public Plugin loadPlugin(final File file) throws InvalidPluginException { ++ public Plugin loadPlugin(File file) throws InvalidPluginException { + Validate.notNull(file, "File cannot be null"); + + if (!file.exists()) { + throw new InvalidPluginException(new FileNotFoundException(file.getPath() + " does not exist")); + } + +- final PluginDescriptionFile description; ++ PluginDescriptionFile description; + try { + description = getPluginDescription(file); + } catch (InvalidDescriptionException ex) { + throw new InvalidPluginException(ex); + } + +- final File parentFile = file.getParentFile(); +- final File dataFolder = new File(parentFile, description.getName()); +- @SuppressWarnings("deprecation") +- final File oldDataFolder = new File(parentFile, description.getRawName()); ++ File dataFolder = new File(file.getParentFile(), description.getName()); ++ File oldDataFolder = getDataFolder(file); + + // Found old data folder + if (dataFolder.equals(oldDataFolder)) { + // They are equal -- nothing needs to be done! + } else if (dataFolder.isDirectory() && oldDataFolder.isDirectory()) { +- server.getLogger().warning(String.format( +- "While loading %s (%s) found old-data folder: `%s' next to the new one `%s'", +- description.getFullName(), ++ server.getLogger().log(Level.INFO, String.format( ++ "While loading %s (%s) found old-data folder: %s next to the new one: %s", ++ description.getName(), + file, + oldDataFolder, + dataFolder + )); + } else if (oldDataFolder.isDirectory() && !dataFolder.exists()) { + if (!oldDataFolder.renameTo(dataFolder)) { +- throw new InvalidPluginException("Unable to rename old data folder: `" + oldDataFolder + "' to: `" + dataFolder + "'"); ++ throw new InvalidPluginException("Unable to rename old data folder: '" + oldDataFolder + "' to: '" + dataFolder + "'"); + } + server.getLogger().log(Level.INFO, String.format( +- "While loading %s (%s) renamed data folder: `%s' to `%s'", +- description.getFullName(), ++ "While loading %s (%s) renamed data folder: '%s' to '%s'", ++ description.getName(), + file, + oldDataFolder, + dataFolder +@@ -104,14 +117,19 @@ + + if (dataFolder.exists() && !dataFolder.isDirectory()) { + throw new InvalidPluginException(String.format( +- "Projected datafolder: `%s' for %s (%s) exists and is not a directory", ++ "Projected datafolder: '%s' for %s (%s) exists and is not a directory", + dataFolder, +- description.getFullName(), ++ description.getName(), + file + )); + } + +- for (final String pluginName : description.getDepend()) { ++ List depend = description.getDepend(); ++ if (depend == null) { ++ depend = ImmutableList.of(); ++ } ++ ++ for (String pluginName : depend) { + if (loaders == null) { + throw new UnknownDependencyException(pluginName); + } +@@ -122,7 +140,7 @@ + } + } + +- final PluginClassLoader loader; ++ PluginClassLoader loader; + try { + loader = new PluginClassLoader(this, getClass().getClassLoader(), description, dataFolder, file); + } catch (InvalidPluginException ex) { +@@ -136,6 +154,26 @@ + return loader.plugin; + } + ++ private File getDataFolder(File file) { ++ File dataFolder = null; ++ ++ String filename = file.getName(); ++ int index = file.getName().lastIndexOf("."); ++ ++ if (index != -1) { ++ String name = filename.substring(0, index); ++ ++ dataFolder = new File(file.getParentFile(), name); ++ } else { ++ // This is if there is no extension, which should not happen ++ // Using _ to prevent name collision ++ ++ dataFolder = new File(file.getParentFile(), filename + "_"); ++ } ++ ++ return dataFolder; ++ } ++ + public PluginDescriptionFile getPluginDescription(File file) throws InvalidDescriptionException { + Validate.notNull(file, "File cannot be null"); + +@@ -283,13 +321,19 @@ + } + } + ++ final CustomTimingsHandler timings = new CustomTimingsHandler("Plugin: " + plugin.getDescription().getFullName() + " Event: " + listener.getClass().getName() + "::" + method.getName()+"("+eventClass.getSimpleName()+")", pluginParentTimer); // Spigot + EventExecutor executor = new EventExecutor() { + public void execute(Listener listener, Event event) throws EventException { + try { + if (!eventClass.isAssignableFrom(event.getClass())) { + return; + } ++ // Spigot start ++ boolean isAsync = event.isAsynchronous(); ++ if (!isAsync) timings.startTiming(); + method.invoke(listener, event); ++ if (!isAsync) timings.stopTiming(); ++ // Spigot end + } catch (InvocationTargetException ex) { + throw new EventException(ex.getCause()); + } catch (Throwable t) { +@@ -297,7 +341,7 @@ + } + } + }; +- if (useTimings) { ++ if (false) { // Spigot - RL handles useTimings check now + eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); + } else { + eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); +@@ -362,4 +406,44 @@ + } + } + } ++ ++ // Cauldron start ++ private InheritanceMap globalInheritanceMap = null; ++ ++ /** ++ * Get the inheritance map for remapping all plugins ++ */ ++ public InheritanceMap getGlobalInheritanceMap() { ++ if (globalInheritanceMap == null) { ++ Map relocationsCurrent = new HashMap(); ++ relocationsCurrent.put("net.minecraft.server", "net.minecraft.server."+PluginClassLoader.getNativeVersion()); ++ JarMapping currentMappings = new JarMapping(); ++ ++ try { ++ currentMappings.loadMappings( ++ new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("mappings/"+PluginClassLoader.getNativeVersion()+"/cb2numpkg.srg"))), ++ new MavenShade(relocationsCurrent), ++ null, false); ++ } catch (IOException ex) { ++ ex.printStackTrace(); ++ throw new RuntimeException(ex); ++ } ++ ++ BiMap inverseClassMap = HashBiMap.create(currentMappings.classes).inverse(); ++ globalInheritanceMap = new InheritanceMap(); ++ ++ BufferedReader reader = new BufferedReader(new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("mappings/"+PluginClassLoader.getNativeVersion()+"/nms.inheritmap"))); ++ ++ try { ++ globalInheritanceMap.load(reader, inverseClassMap); ++ } catch (IOException ex) { ++ ex.printStackTrace(); ++ throw new RuntimeException(ex); ++ } ++ System.out.println("Loaded inheritance map of "+globalInheritanceMap.size()+" classes"); ++ } ++ ++ return globalInheritanceMap; ++ } ++ // Cauldron end + } diff --git a/patches/org/bukkit/plugin/java/PluginClassLoader.java.patch b/patches/org/bukkit/plugin/java/PluginClassLoader.java.patch new file mode 100644 index 0000000..cb028a5 --- /dev/null +++ b/patches/org/bukkit/plugin/java/PluginClassLoader.java.patch @@ -0,0 +1,486 @@ +--- ../src-base/minecraft/org/bukkit/plugin/java/PluginClassLoader.java ++++ ../src-work/minecraft/org/bukkit/plugin/java/PluginClassLoader.java +@@ -1,5 +1,23 @@ + package org.bukkit.plugin.java; + ++// Cauldron start ++import net.md_5.specialsource.provider.ClassLoaderProvider; ++import net.md_5.specialsource.transformer.MavenShade; ++//import org.bouncycastle.util.io.Streams; ++import net.md_5.specialsource.*; ++import net.md_5.specialsource.repo.*; ++import net.minecraft.server.MinecraftServer; ++import net.minecraftforge.cauldron.configuration.CauldronConfig; ++import net.minecraftforge.cauldron.CauldronUtils; ++ ++import org.bukkit.plugin.PluginDescriptionFile; ++import java.io.*; ++import java.net.JarURLConnection; ++import java.security.CodeSigner; ++import java.security.CodeSource; ++import java.util.concurrent.*; ++// Cauldron end ++ + import java.io.File; + import java.net.MalformedURLException; + import java.net.URL; +@@ -15,16 +33,40 @@ + /** + * A ClassLoader for plugins, to allow shared classes across multiple plugins + */ +-final class PluginClassLoader extends URLClassLoader { ++public class PluginClassLoader extends URLClassLoader { + private final JavaPluginLoader loader; +- private final Map> classes = new HashMap>(); ++ private final ConcurrentMap> classes = new ConcurrentHashMap>(); // Cauldron - Threadsafe classloading + private final PluginDescriptionFile description; + private final File dataFolder; + private final File file; +- final JavaPlugin plugin; ++ JavaPlugin plugin; // Cauldron - remove final + private JavaPlugin pluginInit; + private IllegalStateException pluginState; ++ // Cauldron start ++ private JarRemapper remapper; // class remapper for this plugin, or null ++ private RemapperProcessor remapperProcessor; // secondary; for inheritance & remapping reflection ++ private boolean debug; // classloader debugging ++ private int remapFlags = -1; + ++ private static ConcurrentMap jarMappings = new ConcurrentHashMap(); ++ private static final int F_GLOBAL_INHERIT = 1 << 1; ++ private static final int F_REMAP_OBCPRE = 1 << 2; ++ private static final int F_REMAP_NMS152 = 1 << 3; ++ private static final int F_REMAP_NMS164 = 1 << 4; ++ private static final int F_REMAP_NMS172 = 1 << 5; ++ private static final int F_REMAP_NMS179 = 1 << 6; ++ private static final int F_REMAP_NMS1710 = 1 << 7; ++ private static final int F_REMAP_OBC152 = 1 << 8; ++ private static final int F_REMAP_OBC164 = 1 << 9; ++ private static final int F_REMAP_OBC172 = 1 << 10; ++ private static final int F_REMAP_OBC179 = 1 << 11; ++ private static final int F_REMAP_OBC1710 = 1 << 12; ++ private static final int F_REMAP_NMSPRE_MASK= 0xffff0000; // "unversioned" NMS plugin version ++ ++ // This trick bypasses Maven Shade's package rewriting when using String literals [same trick in jline] ++ private static final String org_bukkit_craftbukkit = new String(new char[] {'o','r','g','/','b','u','k','k','i','t','/','c','r','a','f','t','b','u','k','k','i','t'}); ++ // Cauldron end ++ + PluginClassLoader(final JavaPluginLoader loader, final ClassLoader parent, final PluginDescriptionFile description, final File dataFolder, final File file) throws InvalidPluginException, MalformedURLException { + super(new URL[] {file.toURI().toURL()}, parent); + Validate.notNull(loader, "Loader cannot be null"); +@@ -34,6 +76,113 @@ + this.dataFolder = dataFolder; + this.file = file; + ++ // Cauldron start ++ ++ String pluginName = this.description.getName(); ++ ++ // configure default remapper settings ++ boolean useCustomClassLoader = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.custom-class-loader", true); ++ debug = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.debug", false); ++ boolean remapNMS1710 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-nms-v1_7_R4", true); ++ boolean remapNMS179 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-nms-v1_7_R3", true); ++ boolean remapNMS172 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-nms-v1_7_R1", true); ++ boolean remapNMS164 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-nms-v1_6_R3", true); ++ boolean remapNMS152 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-nms-v1_5_R3", true); ++ String remapNMSPre = MinecraftServer.getServer().cauldronConfig.getString("plugin-settings.default.remap-nms-pre", "false"); ++ boolean remapOBC1710 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-obc-v1_7_R4", true); ++ boolean remapOBC179 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-obc-v1_7_R3", true); ++ boolean remapOBC172 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-obc-v1_7_R1", true); ++ boolean remapOBC164 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-obc-v1_6_R3", true); ++ boolean remapOBC152 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-obc-v1_5_R3", true); ++ boolean remapOBCPre = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-obc-pre", false); ++ boolean globalInherit = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.global-inheritance", true); ++ boolean pluginInherit = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.plugin-inheritance", true); ++ boolean reflectFields = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-reflect-field", true); ++ boolean reflectClass = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-reflect-class", true); ++ boolean allowFuture = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings.default.remap-allow-future", false); ++ ++ // plugin-specific overrides ++ useCustomClassLoader = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".custom-class-loader", useCustomClassLoader, false); ++ debug = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".debug", debug, false); ++ remapNMS1710 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-nms-v1_7_R4", remapNMS1710, false); ++ remapNMS179 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-nms-v1_7_R3", remapNMS179, false); ++ remapNMS172 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-nms-v1_7_R1", remapNMS172, false); ++ remapNMS164 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-nms-v1_6_R3", remapNMS164, false); ++ remapNMS152 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-nms-v1_5_R3", remapNMS152, false); ++ remapNMSPre = MinecraftServer.getServer().cauldronConfig.getString("plugin-settings."+pluginName+".remap-nms-pre", remapNMSPre, false); ++ remapOBC1710 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-obc-v1_7_R4", remapOBC1710, false); ++ remapOBC179 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-obc-v1_7_R3", remapOBC179, false); ++ remapOBC172 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-obc-v1_7_R1", remapOBC172, false); ++ remapOBC164 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-obc-v1_6_R3", remapOBC164, false); ++ remapOBC152 = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-obc-v1_5_R3", remapOBC152, false); ++ remapOBCPre = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-obc-pre", remapOBCPre, false); ++ globalInherit = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".global-inheritance", globalInherit, false); ++ pluginInherit = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".plugin-inheritance", pluginInherit, false); ++ reflectFields = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-reflect-field", reflectFields, false); ++ reflectClass = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-reflect-class", reflectClass, false); ++ allowFuture = MinecraftServer.getServer().cauldronConfig.getBoolean("plugin-settings."+pluginName+".remap-allow-future", allowFuture, false); ++ ++ if (debug) { ++ System.out.println("PluginClassLoader debugging enabled for "+pluginName); ++ } ++ ++ if (!useCustomClassLoader) { ++ remapper = null; ++ return; ++ } ++ ++ int flags = 0; ++ if (remapNMS1710) flags |= F_REMAP_NMS1710; ++ if (remapNMS179) flags |= F_REMAP_NMS179; ++ if (remapNMS172) flags |= F_REMAP_NMS172; ++ if (remapNMS164) flags |= F_REMAP_NMS164; ++ if (remapNMS152) flags |= F_REMAP_NMS152; ++ if (!remapNMSPre.equals("false")) { ++ if (remapNMSPre.equals("1.7.10")) flags |= 0x17100000; ++ else if (remapNMSPre.equals("1.7.9")) flags |= 0x01790000; ++ else if (remapNMSPre.equals("1.7.2")) flags |= 0x01720000; ++ else if (remapNMSPre.equals("1.6.4")) flags |= 0x01640000; ++ else if (remapNMSPre.equals("1.5.2")) flags |= 0x01520000; ++ else { ++ System.out.println("Unsupported nms-remap-pre version '"+remapNMSPre+"', disabling"); ++ } ++ } ++ if (remapOBC1710) flags |= F_REMAP_OBC1710; ++ if (remapOBC179) flags |= F_REMAP_OBC179; ++ if (remapOBC172) flags |= F_REMAP_OBC172; ++ if (remapOBC164) flags |= F_REMAP_OBC164; ++ if (remapOBC152) flags |= F_REMAP_OBC152; ++ if (remapOBCPre) flags |= F_REMAP_OBCPRE; ++ if (globalInherit) flags |= F_GLOBAL_INHERIT; ++ ++ remapFlags = flags; // used in findClass0 ++ JarMapping jarMapping = getJarMapping(flags); ++ ++ // Load inheritance map ++ if ((flags & F_GLOBAL_INHERIT) != 0) { ++ if (debug) { ++ System.out.println("Enabling global inheritance remapping"); ++ //ClassLoaderProvider.verbose = debug; // TODO: changed in https://github.com/md-5/SpecialSource/commit/132584eda4f0860c9d14f4c142e684a027a128b8#L3L48 ++ } ++ jarMapping.setInheritanceMap(loader.getGlobalInheritanceMap()); ++ jarMapping.setFallbackInheritanceProvider(new ClassLoaderProvider(this)); ++ } ++ ++ remapper = new JarRemapper(jarMapping); ++ ++ if (pluginInherit || reflectFields || reflectClass) { ++ remapperProcessor = new RemapperProcessor( ++ pluginInherit ? loader.getGlobalInheritanceMap() : null, ++ (reflectFields || reflectClass) ? jarMapping : null); ++ ++ remapperProcessor.setRemapReflectField(reflectFields); ++ remapperProcessor.setRemapReflectClass(reflectClass); ++ remapperProcessor.debug = debug; ++ } else { ++ remapperProcessor = null; ++ } ++ // Cauldron end ++ + try { + Class jarClass; + try { +@@ -58,34 +207,290 @@ + } + + @Override +- protected Class findClass(String name) throws ClassNotFoundException { ++ public Class findClass(String name) throws ClassNotFoundException { // Cauldron - public access for plugins to support CB NMS -> MCP class remap + return findClass(name, true); + } + +- Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { +- if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) { +- throw new ClassNotFoundException(name); ++ // Cauldron start ++ /** ++ * Get the "native" obfuscation version, from our Maven shading version. ++ */ ++ public static String getNativeVersion() { ++ // see https://github.com/mbax/VanishNoPacket/blob/master/src/main/java/org/kitteh/vanish/compat/NMSManager.java ++ if (CauldronUtils.deobfuscatedEnvironment()) return "v1_7_R4"; // support plugins in deobf environment ++ final String packageName = org.bukkit.craftbukkit.CraftServer.class.getPackage().getName(); ++ return packageName.substring(packageName.lastIndexOf('.') + 1); ++ } ++ ++ /** ++ * Load NMS mappings from CraftBukkit mc-dev to repackaged srgnames for FML runtime deobf ++ * ++ * @param jarMapping An existing JarMappings instance to load into ++ * @param obfVersion CraftBukkit version with internal obfuscation counter identifier ++ * >=1.4.7 this is the major version + R#. v1_4_R1=1.4.7, v1_5_R1=1.5, v1_5_R2=1.5.1.. ++ * For older versions (including pre-safeguard) it is the full Minecraft version number ++ * @throws IOException ++ */ ++ private void loadNmsMappings(JarMapping jarMapping, String obfVersion) throws IOException { ++ Map relocations = new HashMap(); ++ // mc-dev jar to CB, apply version shading (aka plugin safeguard) ++ relocations.put("net.minecraft.server", "net.minecraft.server." + obfVersion); ++ ++ // support for running 1.7.10 plugins in Cauldron dev ++ if (CauldronUtils.deobfuscatedEnvironment() && obfVersion.equals("v1_7_R4")) ++ { ++ jarMapping.loadMappings( ++ new BufferedReader(new InputStreamReader(loader.getClass().getClassLoader().getResourceAsStream("mappings/"+obfVersion+"/cb2pkgmcp.srg"))), ++ new MavenShade(relocations), ++ null, false); ++ ++ jarMapping.loadMappings( ++ new BufferedReader(new InputStreamReader(loader.getClass().getClassLoader().getResourceAsStream("mappings/"+obfVersion+"/obf2pkgmcp.srg"))), ++ null, // no version relocation for obf ++ null, false); ++ // resolve naming conflict in FML/CB ++ jarMapping.methods.put("net/minecraft/server/"+obfVersion+"/PlayerConnection/getPlayer ()Lorg/bukkit/craftbukkit/entity/CraftPlayer;", "getPlayerB"); + } +- Class result = classes.get(name); ++ else ++ { ++ jarMapping.loadMappings( ++ new BufferedReader(new InputStreamReader(loader.getClass().getClassLoader().getResourceAsStream("mappings/"+obfVersion+"/cb2numpkg.srg"))), ++ new MavenShade(relocations), ++ null, false); + +- if (result == null) { +- if (checkGlobal) { +- result = loader.getClassByName(name); ++ if (obfVersion.equals("v1_7_R4")) { ++ jarMapping.loadMappings( ++ new BufferedReader(new InputStreamReader(loader.getClass().getClassLoader().getResourceAsStream("mappings/"+obfVersion+"/obf2numpkg.srg"))), ++ null, // no version relocation for obf ++ null, false); + } + ++ // resolve naming conflict in FML/CB ++ jarMapping.methods.put("net/minecraft/server/"+obfVersion+"/PlayerConnection/getPlayer ()Lorg/bukkit/craftbukkit/"+getNativeVersion()+"/entity/CraftPlayer;", "getPlayerB"); ++ } ++ // remap bouncycastle to Forge's included copy, not the vanilla obfuscated copy (not in Cauldron), see #133 ++ //jarMapping.packages.put("net/minecraft/"+obfVersion+"/org/bouncycastle", "org/bouncycastle"); No longer needed ++ } ++ ++ private JarMapping getJarMapping(int flags) { ++ JarMapping jarMapping = jarMappings.get(flags); ++ ++ if (jarMapping != null) { ++ if (debug) { ++ System.out.println("Mapping reused for "+Integer.toHexString(flags)); ++ } ++ return jarMapping; ++ } ++ ++ jarMapping = new JarMapping(); ++ try { ++ ++ // Guava 10 is part of the Bukkit API, so plugins can use it, but FML includes Guava 15 ++ // To resolve this conflict, remap plugin usages to Guava 10 in a separate package ++ // Most plugins should keep this enabled, unless they want a newer Guava ++ jarMapping.packages.put("com/google/common", "guava10/com/google/common"); ++ jarMapping.packages.put(org_bukkit_craftbukkit + "/libs/com/google/gson", "com/google/gson"); // Handle Gson being in a "normal" place ++ // Bukkit moves these packages to nms while we keep them in root so we must relocate them for plugins that rely on them ++ jarMapping.packages.put("net/minecraft/util/io", "io"); ++ jarMapping.packages.put("net/minecraft/util/com", "com"); ++ jarMapping.packages.put("net/minecraft/util/gnu", "gnu"); ++ jarMapping.packages.put("net/minecraft/util/org", "org"); ++ ++ if ((flags & F_REMAP_NMS1710) != 0) { ++ loadNmsMappings(jarMapping, "v1_7_R4"); ++ } ++ ++ if ((flags & F_REMAP_NMS179) != 0) { ++ loadNmsMappings(jarMapping, "v1_7_R3"); ++ } ++ ++ if ((flags & F_REMAP_NMS172) != 0) { ++ loadNmsMappings(jarMapping, "v1_7_R1"); ++ } ++ ++ if ((flags & F_REMAP_NMS164) != 0) { ++ loadNmsMappings(jarMapping, "v1_6_R3"); ++ } ++ ++ if ((flags & F_REMAP_NMS152) != 0) { ++ loadNmsMappings(jarMapping, "v1_5_R3"); ++ } ++ ++ if ((flags & F_REMAP_OBC1710) != 0) { ++ if (CauldronUtils.deobfuscatedEnvironment()) ++ jarMapping.packages.put(org_bukkit_craftbukkit+"/v1_7_R4", org_bukkit_craftbukkit); ++ else jarMapping.packages.put(org_bukkit_craftbukkit+"/v1_7_R4", org_bukkit_craftbukkit+"/"+getNativeVersion()); ++ } ++ ++ if ((flags & F_REMAP_OBC179) != 0) { ++ if (CauldronUtils.deobfuscatedEnvironment()) ++ jarMapping.packages.put(org_bukkit_craftbukkit+"/v1_7_R3", org_bukkit_craftbukkit); ++ else jarMapping.packages.put(org_bukkit_craftbukkit+"/v1_7_R3", org_bukkit_craftbukkit+"/"+getNativeVersion()); ++ } ++ ++ if ((flags & F_REMAP_OBC172) != 0) { ++ if (CauldronUtils.deobfuscatedEnvironment()) ++ jarMapping.packages.put(org_bukkit_craftbukkit+"/v1_7_R1", org_bukkit_craftbukkit+"/"+getNativeVersion()); ++ else jarMapping.packages.put(org_bukkit_craftbukkit+"/v1_7_R1", org_bukkit_craftbukkit+"/"+getNativeVersion()); ++ } ++ ++ if ((flags & F_REMAP_OBC164) != 0) { ++ jarMapping.packages.put(org_bukkit_craftbukkit+"/v1_6_R3", org_bukkit_craftbukkit+"/"+getNativeVersion()); ++ } ++ ++ if ((flags & F_REMAP_OBC152) != 0) { ++ jarMapping.packages.put(org_bukkit_craftbukkit+"/v1_5_R3", org_bukkit_craftbukkit+"/"+getNativeVersion()); ++ } ++ ++ if ((flags & F_REMAP_OBCPRE) != 0) { ++ // enabling unversioned obc not currently compatible with versioned obc plugins (overmapped) - ++ // admins should enable remap-obc-pre on a per-plugin basis, as needed ++ // then map unversioned to current version ++ jarMapping.packages.put(org_bukkit_craftbukkit+"/libs/org/objectweb/asm", "org/objectweb/asm"); // ? ++ jarMapping.packages.put(org_bukkit_craftbukkit, org_bukkit_craftbukkit+"/"+getNativeVersion()); ++ } ++ ++ if ((flags & F_REMAP_NMSPRE_MASK) != 0) { ++ String obfVersion; ++ switch (flags & F_REMAP_NMSPRE_MASK) ++ { ++ case 0x17100000: obfVersion = "v1_7_R4"; break; ++ case 0x01790000: obfVersion = "v1_7_R3"; break; ++ case 0x01720000: obfVersion = "v1_7_R1"; break; ++ case 0x01640000: obfVersion = "v1_6_R3"; break; ++ case 0x01510000: obfVersion = "v1_5_R2"; break; ++ default: throw new IllegalArgumentException("Invalid unversioned mapping flags: "+Integer.toHexString(flags & F_REMAP_NMSPRE_MASK)+" in "+Integer.toHexString(flags)); ++ } ++ ++ jarMapping.loadMappings( ++ new BufferedReader(new InputStreamReader(loader.getClass().getClassLoader().getResourceAsStream("mappings/" + obfVersion + "/cb2numpkg.srg"))), ++ null, // no version relocation! ++ null, false); ++ } ++ ++ System.out.println("Mapping loaded "+jarMapping.packages.size()+" packages, "+jarMapping.classes.size()+" classes, "+jarMapping.fields.size()+" fields, "+jarMapping.methods.size()+" methods, flags "+Integer.toHexString(flags)); ++ ++ JarMapping currentJarMapping = jarMappings.putIfAbsent(flags, jarMapping); ++ return currentJarMapping == null ? jarMapping : currentJarMapping; ++ } catch (IOException ex) { ++ ex.printStackTrace(); ++ throw new RuntimeException(ex); ++ } ++ } ++ ++ Class findClass(String name, boolean checkGlobal) throws ClassNotFoundException { ++ // Cauldron start - remap any calls for classes with packaged nms version ++ if (name.startsWith("net.minecraft.")) ++ { ++ JarMapping jarMapping = this.getJarMapping(remapFlags); // grab from SpecialSource ++ String remappedClass = jarMapping.classes.get(name.replaceAll("\\.", "\\/")); // get remapped pkgmcp class name ++ Class clazz = ((net.minecraft.launchwrapper.LaunchClassLoader)MinecraftServer.getServer().getClass().getClassLoader()).findClass(remappedClass); ++ return clazz; ++ } ++ if (name.startsWith("org.bukkit.")) { ++ if (debug) { ++ System.out.println("Unexpected plugin findClass on OBC/NMS: name="+name+", checkGlobal="+checkGlobal+"; returning not found"); ++ } ++ throw new ClassNotFoundException(name); ++ } ++ // custom loader, if enabled, threadsafety ++ synchronized (name.intern()) { ++ Class result = classes.get(name); ++ + if (result == null) { +- result = super.findClass(name); ++ if (checkGlobal) { ++ result = loader.getClassByName(name); // Don't warn on deprecation, but maintain overridability ++ } + ++ if (result == null) { ++ if (remapper == null) { ++ result = super.findClass(name); ++ } else { ++ result = remappedFindClass(name); ++ } ++ ++ if (result != null) { ++ loader.setClass(name, result); ++ } ++ } + if (result != null) { +- loader.setClass(name, result); ++ Class old = classes.putIfAbsent(name, result); ++ if (old != null && old != result) { ++ System.err.println("Defined class " + name + " twice as different classes, " + result + " and " + old); ++ result = old; ++ } + } + } + +- classes.put(name, result); ++ return result; + } ++ // Cauldron end ++ } ++ private Class remappedFindClass(String name) throws ClassNotFoundException { ++ Class result = null; + ++ try { ++ // Load the resource to the name ++ String path = name.replace('.', '/').concat(".class"); ++ URL url = this.findResource(path); ++ if (url != null) { ++ InputStream stream = url.openStream(); ++ if (stream != null) { ++ byte[] bytecode = null; ++ ++ // Reflection remap and inheritance extract ++ if (remapperProcessor != null) { ++ // add to inheritance map ++ bytecode = remapperProcessor.process(stream); ++ if (bytecode == null) stream = url.openStream(); ++ } ++ ++ /*if (bytecode == null) { ++ bytecode = Streams.readAll(stream); ++ }*/ ++ ++ // Remap the classes ++ byte[] remappedBytecode = remapper.remapClassFile(bytecode, RuntimeRepo.getInstance()); ++ ++ if (debug) { ++ File file = new File("remapped-plugin-classes/"+name+".class"); ++ file.getParentFile().mkdirs(); ++ try { ++ FileOutputStream fileOutputStream = new FileOutputStream(file); ++ fileOutputStream.write(remappedBytecode); ++ fileOutputStream.close(); ++ } catch (IOException ex) { ++ ex.printStackTrace(); ++ } ++ } ++ ++ // Define (create) the class using the modified byte code ++ // The top-child class loader is used for this to prevent access violations ++ // Set the codesource to the jar, not within the jar, for compatibility with ++ // plugins that do new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI())) ++ // instead of using getResourceAsStream - see https://github.com/MinecraftPortCentral/Cauldron-Plus/issues/75 ++ JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); // parses only ++ URL jarURL = jarURLConnection.getJarFileURL(); ++ CodeSource codeSource = new CodeSource(jarURL, new CodeSigner[0]); ++ ++ result = this.defineClass(name, remappedBytecode, 0, remappedBytecode.length, codeSource); ++ if (result != null) { ++ // Resolve it - sets the class loader of the class ++ this.resolveClass(result); ++ } ++ } ++ } ++ } catch (Throwable t) { ++ if (debug) { ++ System.out.println("remappedFindClass("+name+") exception: "+t); ++ t.printStackTrace(); ++ } ++ throw new ClassNotFoundException("Failed to remap class "+name, t); ++ } ++ + return result; + } ++ // Cauldron end + + Set getClasses() { + return classes.keySet(); diff --git a/patches/org/bukkit/plugin/messaging/Messenger.java.patch b/patches/org/bukkit/plugin/messaging/Messenger.java.patch new file mode 100644 index 0000000..5b70737 --- /dev/null +++ b/patches/org/bukkit/plugin/messaging/Messenger.java.patch @@ -0,0 +1,11 @@ +--- ../src-base/minecraft/org/bukkit/plugin/messaging/Messenger.java ++++ ../src-work/minecraft/org/bukkit/plugin/messaging/Messenger.java +@@ -18,7 +18,7 @@ + /** + * Represents the largest size that a Plugin Channel may be. + */ +- public static final int MAX_CHANNEL_SIZE = 16; ++ public static final int MAX_CHANNEL_SIZE = 20; // Cauldron - Vanilla increased limit of C17PacketCustomPayload size from 16 -> 20 in 1.7 + + /** + * Checks if the specified channel is a reserved name. diff --git a/patches/org/bukkit/plugin/messaging/StandardMessenger.java.patch b/patches/org/bukkit/plugin/messaging/StandardMessenger.java.patch new file mode 100644 index 0000000..c440163 --- /dev/null +++ b/patches/org/bukkit/plugin/messaging/StandardMessenger.java.patch @@ -0,0 +1,19 @@ +--- ../src-base/minecraft/org/bukkit/plugin/messaging/StandardMessenger.java ++++ ../src-work/minecraft/org/bukkit/plugin/messaging/StandardMessenger.java +@@ -421,7 +421,15 @@ + Set registrations = getIncomingChannelRegistrations(channel); + + for (PluginMessageListenerRegistration registration : registrations) { +- registration.getListener().onPluginMessageReceived(channel, source, message); ++ // Spigot Start ++ try ++ { ++ registration.getListener().onPluginMessageReceived( channel, source, message ); ++ } catch ( Throwable t ) ++ { ++ org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.WARNING, "Could not pass incoming plugin message to " + registration.getPlugin(), t ); ++ } ++ // Spigot End + } + } + diff --git a/patches/org/bukkit/potion/PotionEffectType.java.patch b/patches/org/bukkit/potion/PotionEffectType.java.patch new file mode 100644 index 0000000..9a04c2c --- /dev/null +++ b/patches/org/bukkit/potion/PotionEffectType.java.patch @@ -0,0 +1,57 @@ +--- ../src-base/minecraft/org/bukkit/potion/PotionEffectType.java ++++ ../src-work/minecraft/org/bukkit/potion/PotionEffectType.java +@@ -202,7 +202,7 @@ + return "PotionEffectType[" + id + ", " + getName() + "]"; + } + +- private static final PotionEffectType[] byId = new PotionEffectType[24]; ++ private static final Map byId = new HashMap(); // Cauldron change underlying storage to map + private static final Map byName = new HashMap(); + // will break on updates. + private static boolean acceptingNew = true; +@@ -216,9 +216,9 @@ + */ + @Deprecated + public static PotionEffectType getById(int id) { +- if (id >= byId.length || id < 0) ++ if (id >= byId.size() || id < 0) // Cauldron + return null; +- return byId[id]; ++ return byId.get(id); // Cauldron + } + + /** +@@ -240,15 +240,18 @@ + * @param type PotionType to register + */ + public static void registerPotionEffectType(PotionEffectType type) { ++ // Cauldron start - allow vanilla to replace potions ++ /* + if (byId[type.id] != null || byName.containsKey(type.getName().toLowerCase())) { + throw new IllegalArgumentException("Cannot set already-set type"); + } else if (!acceptingNew) { + throw new IllegalStateException( + "No longer accepting new potion effect types (can only be done by the server implementation)"); + } +- +- byId[type.id] = type; ++ */ ++ byId.put(type.id, type); + byName.put(type.getName().toLowerCase(), type); ++ // Cauldron end + } + + /** +@@ -264,6 +267,11 @@ + * @return Array of types. + */ + public static PotionEffectType[] values() { +- return byId.clone(); ++ // Cauldron start ++ int maxId = 0; ++ for(int id : byId.keySet()) maxId = Math.max(maxId, id); ++ PotionEffectType[] result = new PotionEffectType[maxId + 1]; ++ return byId.values().toArray(result); // Cauldron change underlying storage to map ++ // Cauldron end + } + } diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..b7ee386 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'cauldron' \ No newline at end of file diff --git a/src/main/guava10.zip b/src/main/guava10.zip new file mode 100644 index 0000000..cbb0a4d Binary files /dev/null and b/src/main/guava10.zip differ diff --git a/src/main/java/jline/AnsiWindowsTerminal.java b/src/main/java/jline/AnsiWindowsTerminal.java new file mode 100644 index 0000000..a638036 --- /dev/null +++ b/src/main/java/jline/AnsiWindowsTerminal.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009 the original author(s). + * + * 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. + * + * MODIFICATIONS: methods to deal with wrapping the output stream. + */ + +package jline; + +import org.fusesource.jansi.AnsiConsole; +import org.fusesource.jansi.AnsiOutputStream; +import org.fusesource.jansi.WindowsAnsiOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; + +/** + * ANSI-supported {@link WindowsTerminal}. + * + * @since 2.0 + */ +public class AnsiWindowsTerminal + extends WindowsTerminal +{ + private final boolean ansiSupported = detectAnsiSupport(); + + @Override + public OutputStream wrapOutIfNeeded(OutputStream out) { + return wrapOutputStream(out); + } + + /** + * Returns an ansi output stream handler. We return whatever was + * passed if we determine we cannot handle ansi based on Kernel32 calls. + * + * @return an @{link AltWindowAnsiOutputStream} instance or the passed + * stream. + */ + private static OutputStream wrapOutputStream(final OutputStream stream) { + String os = System.getProperty("os.name"); + if( os.startsWith("Windows") ) { + // On windows we know the console does not interpret ANSI codes.. + try { + return new WindowsAnsiOutputStream(stream); + } catch (Throwable ignore) { + // this happens when JNA is not in the path.. or + // this happens when the stdout is being redirected to a file. + } + // Use the ANSIOutputStream to strip out the ANSI escape sequences. + return new AnsiOutputStream(stream); + } + return stream; + } + + private static boolean detectAnsiSupport() { + AnsiConsole.systemInstall(); // CraftBukkit - install Windows JNI library + OutputStream out = AnsiConsole.wrapOutputStream(new ByteArrayOutputStream()); + try { + out.close(); + } + catch (Exception e) { + // ignore; + } + return out instanceof WindowsAnsiOutputStream; + } + + public AnsiWindowsTerminal() throws Exception { + super(); + } + + @Override + public boolean isAnsiSupported() { + return ansiSupported; + } + + @Override + public boolean hasWeirdWrap() { + return false; + } +} diff --git a/src/main/java/jline/internal/TerminalLineSettings.java b/src/main/java/jline/internal/TerminalLineSettings.java new file mode 100644 index 0000000..b4b2409 --- /dev/null +++ b/src/main/java/jline/internal/TerminalLineSettings.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + */ + +package jline.internal; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Provides access to terminal line settings via stty. + * + * @author Marc Prud'hommeaux + * @author Dale Kemp + * @author Jason Dillon + * @author Jean-Baptiste Onofré + * @since 2.0 + */ +public final class TerminalLineSettings +{ + public static final String JLINE_STTY = "jline.stty"; + + public static final String DEFAULT_STTY = "stty"; + + public static final String JLINE_SH = "jline.sh"; + + public static final String DEFAULT_SH = "sh"; + + private String sttyCommand; + + private String shCommand; + + private String config; + + private long configLastFetched; + + public TerminalLineSettings() throws IOException, InterruptedException { + sttyCommand = Configuration.getString(JLINE_STTY, DEFAULT_STTY); + shCommand = Configuration.getString(JLINE_SH, DEFAULT_SH); + config = get("-a"); + configLastFetched = System.currentTimeMillis(); + + Log.debug("Config: ", config); + + // sanity check + if (config.length() == 0) { + throw new IOException(MessageFormat.format("Unrecognized stty code: {0}", config)); + } + } + + public String getConfig() { + return config; + } + + public void restore() throws IOException, InterruptedException { + set("sane"); + } + + public String get(final String args) throws IOException, InterruptedException { + return stty(args); + } + + public void set(final String args) throws IOException, InterruptedException { + stty(args); + } + + /** + *

+ * Get the value of a stty property, including the management of a cache. + *

+ * + * @param name the stty property. + * @return the stty property value. + */ + public int getProperty(String name) { + assert name != null; + // CraftBukkit start + long currentTime = System.currentTimeMillis(); + + try { + // tty properties are cached so we don't have to worry too much about getting term widht/height + if (config == null || currentTime - configLastFetched > 1000) { + config = get("-a"); + } + } catch (Exception e) { + Log.debug("Failed to query stty ", name, "\n", e); + } + + // always update the last fetched time and try to parse the output + if (currentTime - configLastFetched > 1000) { + configLastFetched = currentTime; + } + + return this.getProperty(name, config); + // CraftBukkit end + } + + /** + *

+ * Parses a stty output (provided by stty -a) and return the value of a given property. + *

+ * + * @param name property name. + * @param stty string resulting of stty -a execution. + * @return value of the given property. + */ + protected static int getProperty(String name, String stty) { + // try the first kind of regex + Pattern pattern = Pattern.compile(name + "\\s+=\\s+([^;]*)[;\\n\\r]"); + Matcher matcher = pattern.matcher(stty); + if (!matcher.find()) { + // try a second kind of regex + pattern = Pattern.compile(name + "\\s+([^;]*)[;\\n\\r]"); + matcher = pattern.matcher(stty); + if (!matcher.find()) { + // try a second try of regex + pattern = Pattern.compile("(\\S*)\\s+" + name); + matcher = pattern.matcher(stty); + if (!matcher.find()) { + return -1; + } + } + } + return parseControlChar(matcher.group(1)); + } + + private static int parseControlChar(String str) { + // under + if ("".equals(str)) { + return -1; + } + // octal + if (str.charAt(0) == '0') { + return Integer.parseInt(str, 8); + } + // decimal + if (str.charAt(0) >= '1' && str.charAt(0) <= '9') { + return Integer.parseInt(str, 10); + } + // control char + if (str.charAt(0) == '^') { + if (str.charAt(1) == '?') { + return 127; + } else { + return str.charAt(1) - 64; + } + } else if (str.charAt(0) == 'M' && str.charAt(1) == '-') { + if (str.charAt(2) == '^') { + if (str.charAt(3) == '?') { + return 127 + 128; + } else { + return str.charAt(3) - 64 + 128; + } + } else { + return str.charAt(2) + 128; + } + } else { + return str.charAt(0); + } + } + + private String stty(final String args) throws IOException, InterruptedException { + assert args != null; + return exec(String.format("%s %s < /dev/tty", sttyCommand, args)); + } + + private String exec(final String cmd) throws IOException, InterruptedException { + assert cmd != null; + return exec(shCommand, "-c", cmd); + } + + private String exec(final String... cmd) throws IOException, InterruptedException { + assert cmd != null; + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + Log.trace("Running: ", cmd); + + Process p = Runtime.getRuntime().exec(cmd); + + InputStream in = null; + InputStream err = null; + OutputStream out = null; + try { + int c; + in = p.getInputStream(); + while ((c = in.read()) != -1) { + bout.write(c); + } + err = p.getErrorStream(); + while ((c = err.read()) != -1) { + bout.write(c); + } + out = p.getOutputStream(); + p.waitFor(); + } + finally { + close(in, out, err); + } + + String result = bout.toString(); + + Log.trace("Result: ", result); + + return result; + } + + private static void close(final Closeable... closeables) { + for (Closeable c : closeables) { + try { + c.close(); + } + catch (Exception e) { + // Ignore + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/entity/EntityMinecartCommandBlockListener.java b/src/main/java/net/minecraft/entity/EntityMinecartCommandBlockListener.java new file mode 100644 index 0000000..1556c90 --- /dev/null +++ b/src/main/java/net/minecraft/entity/EntityMinecartCommandBlockListener.java @@ -0,0 +1,51 @@ +package net.minecraft.entity; + +import io.netty.buffer.ByteBuf; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.command.server.CommandBlockLogic; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.MathHelper; +import net.minecraft.world.World; +// CraftBukkit - package-private -> public +public class EntityMinecartCommandBlockListener extends CommandBlockLogic +{ + final EntityMinecartCommandBlock field_145768_a; + + EntityMinecartCommandBlockListener(EntityMinecartCommandBlock p_i45320_1_) + { + this.field_145768_a = p_i45320_1_; + this.sender = (org.bukkit.craftbukkit.entity.CraftMinecartCommand) p_i45320_1_.getBukkitEntity(); // CraftBukkit - Set the sender + } + + public void func_145756_e() + { + this.field_145768_a.getDataWatcher().updateObject(23, this.func_145753_i()); + this.field_145768_a.getDataWatcher().updateObject(24, IChatComponent.Serializer.func_150696_a(this.func_145749_h())); + } + + /** + * Return the position for this command sender. + */ + public ChunkCoordinates getPlayerCoordinates() + { + return new ChunkCoordinates(MathHelper.floor_double(this.field_145768_a.posX), MathHelper.floor_double(this.field_145768_a.posY + 0.5D), MathHelper.floor_double(this.field_145768_a.posZ)); + } + + public World getEntityWorld() + { + return this.field_145768_a.worldObj; + } + + @SideOnly(Side.CLIENT) + public int func_145751_f() + { + return 1; + } + @SideOnly(Side.CLIENT) + public void func_145757_a(ByteBuf p_145757_1_) + { + p_145757_1_.writeInt(field_145768_a.getEntityId()); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/inventory/ContainerEnchantTableInventory.java b/src/main/java/net/minecraft/inventory/ContainerEnchantTableInventory.java new file mode 100644 index 0000000..71da152 --- /dev/null +++ b/src/main/java/net/minecraft/inventory/ContainerEnchantTableInventory.java @@ -0,0 +1,75 @@ +package net.minecraft.inventory; + +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import net.minecraft.item.ItemStack; +// CraftBukkit end + +public class ContainerEnchantTableInventory extends InventoryBasic // CraftBukkit -> public +{ + /** The brewing stand this slot belongs to. */ + final ContainerEnchantment container; + + // CraftBukkit start + public List transaction = new java.util.ArrayList(); + public org.bukkit.entity.Player player; + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() + { + return this.inventoryContents; + } + + public void onOpen(CraftHumanEntity who) + { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) + { + transaction.remove(who); + } + + public List getViewers() + { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() + { + return this.player; + } + + public void setMaxStackSize(int size) + { + maxStack = size; + } + // CraftBukkit end + + ContainerEnchantTableInventory(ContainerEnchantment par1ContainerEnchantment, String par2Str, boolean par3, int par4) + { + super(par2Str, par3, par4); + this.container = par1ContainerEnchantment; + this.setMaxStackSize(1); // CraftBukkit + } + + /** + * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't + * this more of a set than a get?* + */ + public int getInventoryStackLimit() + { + return maxStack; // CraftBukkit + } + + /** + * Called when an the contents of an Inventory change, usually + */ + public void markDirty() + { + super.markDirty(); + this.container.onCraftMatrixChanged((IInventory) this); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/inventory/ContainerRepairInventory.java b/src/main/java/net/minecraft/inventory/ContainerRepairInventory.java new file mode 100644 index 0000000..28f5b0d --- /dev/null +++ b/src/main/java/net/minecraft/inventory/ContainerRepairInventory.java @@ -0,0 +1,65 @@ +package net.minecraft.inventory; + +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import net.minecraft.item.ItemStack; +// CraftBukkit end + +public class ContainerRepairInventory extends InventoryBasic // CraftBukkit - public +{ + final ContainerRepair repairContainer; + + // CraftBukkit start + public List transaction = new java.util.ArrayList(); + public org.bukkit.entity.Player player; + private int maxStack = MAX_STACK; + + public ItemStack[] getContents() + { + return this.inventoryContents; + } + + public void onOpen(CraftHumanEntity who) + { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) + { + transaction.remove(who); + } + + public List getViewers() + { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() + { + return this.player; + } + + public void setMaxStackSize(int size) + { + maxStack = size; + } + // CraftBukkit end + + ContainerRepairInventory(ContainerRepair par1ContainerRepair, String par2Str, boolean par3, int par4) + { + super(par2Str, par3, par4); + this.repairContainer = par1ContainerRepair; + this.setMaxStackSize(1); // CraftBukkit + } + + /** + * Called when an the contents of an Inventory change, usually + */ + public void markDirty() + { + super.markDirty(); + this.repairContainer.onCraftMatrixChanged((IInventory) this); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/server/network/ThreadPlayerLookupUUID.java b/src/main/java/net/minecraft/server/network/ThreadPlayerLookupUUID.java new file mode 100644 index 0000000..206c259 --- /dev/null +++ b/src/main/java/net/minecraft/server/network/ThreadPlayerLookupUUID.java @@ -0,0 +1,142 @@ +package net.minecraft.server.network; + +import java.math.BigInteger; +import java.util.UUID; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.NetHandlerLoginServer.LoginState; +import net.minecraft.util.CryptManager; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationUnavailableException; + +// CraftBukkit start +import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerPreLoginEvent; +// CraftBukkit end + +class ThreadPlayerLookupUUID extends Thread +{ + final NetHandlerLoginServer field_151292_a; + private MinecraftServer mcServer; // Cauldron + + ThreadPlayerLookupUUID(NetHandlerLoginServer p_i45296_1_, String p_i45296_2_) + { + super(p_i45296_2_); + this.field_151292_a = p_i45296_1_; + this.mcServer = NetHandlerLoginServer.getMinecraftServer(this.field_151292_a); // Cauldron + } + + public void run() + { + GameProfile gameprofile = NetHandlerLoginServer.getGameProfile(this.field_151292_a); + try + { + // Spigot Start + if (!this.mcServer.isServerInOnlineMode()) + { + this.field_151292_a.initUUID(); + fireLoginEvents(); + return; + } + // Spigot End + String s = (new BigInteger(CryptManager.getServerIdHash(NetHandlerLoginServer.getLoginServerId(this.field_151292_a), this.mcServer.getKeyPair().getPublic(), NetHandlerLoginServer.getSecretKey(this.field_151292_a)))).toString(16); + NetHandlerLoginServer.processPlayerLoginGameProfile(this.field_151292_a, this.mcServer.func_147130_as().hasJoinedServer(new GameProfile((UUID)null, gameprofile.getName()), s)); + + if (NetHandlerLoginServer.getGameProfile(this.field_151292_a) != null) + { + fireLoginEvents(); // Spigot + } + else if (this.mcServer.isSinglePlayer()) + { + NetHandlerLoginServer.getLogger().warn("Failed to verify username but will let them in anyway!"); + NetHandlerLoginServer.processPlayerLoginGameProfile(this.field_151292_a, this.field_151292_a.func_152506_a(gameprofile)); + NetHandlerLoginServer.setLoginState(this.field_151292_a, LoginState.READY_TO_ACCEPT); + } + else + { + this.field_151292_a.func_147322_a("Failed to verify username!"); + NetHandlerLoginServer.getLogger().error("Username \'" + NetHandlerLoginServer.getGameProfile(this.field_151292_a).getName() + "\' tried to join with an invalid session"); + } + } + catch (AuthenticationUnavailableException authenticationunavailableexception) + { + if (this.mcServer.isSinglePlayer()) + { + NetHandlerLoginServer.getLogger().warn("Authentication servers are down but will let them in anyway!"); + NetHandlerLoginServer.processPlayerLoginGameProfile(this.field_151292_a, this.field_151292_a.func_152506_a(gameprofile)); + NetHandlerLoginServer.setLoginState(this.field_151292_a, LoginState.READY_TO_ACCEPT); + } + else + { + this.field_151292_a.func_147322_a("Authentication servers are down. Please try again later, sorry!"); + NetHandlerLoginServer.getLogger().error("Couldn\'t verify username because servers are unavailable"); + } + // CraftBukkit start - catch all exceptions + } + catch (Exception exception) + { + this.field_151292_a.func_147322_a("Failed to verify username!"); + this.mcServer.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + NetHandlerLoginServer.getGameProfile(this.field_151292_a).getName(), exception); + // CraftBukkit end + } + } + + private void fireLoginEvents() throws Exception + { + // CraftBukkit start - fire PlayerPreLoginEvent + if (!this.field_151292_a.field_147333_a.isChannelOpen()) + { + return; + } + + String playerName = NetHandlerLoginServer.getGameProfile(this.field_151292_a).getName(); + java.net.InetAddress address = ((java.net.InetSocketAddress) this.field_151292_a.field_147333_a.getSocketAddress()).getAddress(); + java.util.UUID uniqueId = NetHandlerLoginServer.getGameProfile(this.field_151292_a).getId(); + final org.bukkit.craftbukkit.CraftServer server = this.mcServer.server; + + AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId); + server.getPluginManager().callEvent(asyncEvent); + + if (PlayerPreLoginEvent.getHandlerList().getRegisteredListeners().length != 0) + { + final PlayerPreLoginEvent event = new PlayerPreLoginEvent(playerName, address, uniqueId); + + if (asyncEvent.getResult() != PlayerPreLoginEvent.Result.ALLOWED) + { + event.disallow(asyncEvent.getResult(), asyncEvent.getKickMessage()); + } + + Waitable waitable = new Waitable() + { + @Override + protected PlayerPreLoginEvent.Result evaluate() + { + server.getPluginManager().callEvent(event); + return event.getResult(); + } + }; + + NetHandlerLoginServer.getMinecraftServer(this.field_151292_a).processQueue.add(waitable); + + if (waitable.get() != PlayerPreLoginEvent.Result.ALLOWED) + { + this.field_151292_a.func_147322_a(event.getKickMessage()); + return; + } + } + else + { + if (asyncEvent.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) + { + this.field_151292_a.func_147322_a(asyncEvent.getKickMessage()); + return; + } + } + // CraftBukkit end + + NetHandlerLoginServer.getLogger().info("UUID of player " + NetHandlerLoginServer.getGameProfile(this.field_151292_a).getName() + " is " + NetHandlerLoginServer.getGameProfile(this.field_151292_a).getId());; + NetHandlerLoginServer.setLoginState(this.field_151292_a, LoginState.READY_TO_ACCEPT); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/tileentity/TileEntityCommandBlockListener.java b/src/main/java/net/minecraft/tileentity/TileEntityCommandBlockListener.java new file mode 100644 index 0000000..0c5e9ce --- /dev/null +++ b/src/main/java/net/minecraft/tileentity/TileEntityCommandBlockListener.java @@ -0,0 +1,56 @@ +package net.minecraft.tileentity; + +import io.netty.buffer.ByteBuf; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.command.server.CommandBlockLogic; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.world.World; +// CraftBukkit - package-private -> public +public class TileEntityCommandBlockListener extends CommandBlockLogic +{ + final TileEntityCommandBlock field_145767_a; + + TileEntityCommandBlockListener(TileEntityCommandBlock p_i45441_1_) + { + this.field_145767_a = p_i45441_1_; + sender = new org.bukkit.craftbukkit.command.CraftBlockCommandSender(this); // CraftBukkit - add sender + } + + /** + * Return the position for this command sender. + */ + public ChunkCoordinates getPlayerCoordinates() + { + return new ChunkCoordinates(this.field_145767_a.xCoord, this.field_145767_a.yCoord, this.field_145767_a.zCoord); + } + + public World getEntityWorld() + { + return this.field_145767_a.getWorldObj(); + } + + public void func_145752_a(String p_145752_1_) + { + super.func_145752_a(p_145752_1_); + this.field_145767_a.markDirty(); + } + + public void func_145756_e() + { + this.field_145767_a.getWorldObj().markBlockForUpdate(this.field_145767_a.xCoord, this.field_145767_a.yCoord, this.field_145767_a.zCoord); + } + + @SideOnly(Side.CLIENT) + public int func_145751_f() + { + return 0; + } + @SideOnly(Side.CLIENT) + public void func_145757_a(ByteBuf p_145757_1_) + { + p_145757_1_.writeInt(field_145767_a.xCoord); + p_145757_1_.writeInt(field_145767_a.yCoord); + p_145757_1_.writeInt(field_145767_a.zCoord); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/cauldron/CauldronHooks.java b/src/main/java/net/minecraftforge/cauldron/CauldronHooks.java new file mode 100644 index 0000000..ac7ac87 --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/CauldronHooks.java @@ -0,0 +1,469 @@ +package net.minecraftforge.cauldron; + +import gnu.trove.map.hash.TObjectIntHashMap; +import gnu.trove.map.hash.TObjectLongHashMap; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.MBeanServer; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.MathHelper; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.World; +import net.minecraft.world.gen.ChunkProviderServer; + +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; + +import com.google.gson.stream.JsonWriter; + +public class CauldronHooks +{ + // Some mods such as Twilight Forest listen for specific events as their WorldProvider loads to hotload its dimension. This prevents this from happening so MV can create worlds using the same provider without issue. + public static boolean craftWorldLoading = false; + public static int tickingDimension = 0; + public static ChunkCoordIntPair tickingChunk = null; + public static Map, TileEntityCache> tileEntityCache = new HashMap, TileEntityCache>(); + + private static TObjectLongHashMap recentWarnings = new TObjectLongHashMap(); + + public static void logInfo(String msg, Object... args) + { + MinecraftServer.getServer().logInfo(MessageFormat.format(msg, args)); + } + + public static void logWarning(String msg, Object... args) + { + MinecraftServer.getServer().logWarning(MessageFormat.format(msg, args)); + } + + public static void logSevere(String msg, Object... args) + { + MinecraftServer.getServer().logSevere(MessageFormat.format(msg, args)); + } + + public static void logStack() + { + if (MinecraftServer.cauldronConfig.logWithStackTraces.getValue()) + { + Throwable ex = new Throwable(); + ex.fillInStackTrace(); + ex.printStackTrace(); + } + } + + public static void logEntityDeath(Entity entity) + { + if (MinecraftServer.cauldronConfig.entityDeathLogging.getValue()) + { + logInfo("Dim: {0} setDead(): {1}", entity.worldObj.provider.dimensionId, entity); + logStack(); + } + } + + public static void logEntityDespawn(Entity entity, String reason) + { + if (MinecraftServer.cauldronConfig.entityDespawnLogging.getValue()) + { + logInfo("Dim: {0} Despawning ({1}): {2}", entity.worldObj.provider.dimensionId, reason, entity); + //logInfo("Chunk Is Active: {0}", entity.worldObj.inActiveChunk(entity)); + logStack(); + } + } + + public static void logEntitySpawn(World world, Entity entity, SpawnReason spawnReason) + { + if (MinecraftServer.cauldronConfig.entitySpawnLogging.getValue()) + { + logInfo("Dim: {0} Spawning ({1}): {2}", world.provider.dimensionId, spawnReason, entity); + logInfo("Dim: {0} Entities Last Tick: {1}", world.provider.dimensionId, world.entitiesTicked); + logInfo("Dim: {0} Tiles Last Tick: {1}", world.provider.dimensionId, world.tilesTicked); + //logInfo("Chunk Is Active: {0}", world.inActiveChunk(entity)); + logStack(); + } + } + + public static void logChunkLoad(ChunkProviderServer provider, String msg, int x, int z, boolean logLoadOnRequest) + { + if (MinecraftServer.cauldronConfig.chunkLoadLogging.getValue()) + { + logInfo("{0} Chunk At [{1}] ({2}, {3})", msg, provider.worldObj.provider.dimensionId, x, z); + if (logLoadOnRequest) + { + logLoadOnRequest(provider, x, z); + } + logStack(); + } + } + + public static void logChunkUnload(ChunkProviderServer provider, int x, int z, String msg) + { + if (MinecraftServer.cauldronConfig.chunkUnloadLogging.getValue()) + { + logInfo("{0} [{1}] ({2}, {3})", msg, provider.worldObj.provider.dimensionId, x, z); + long currentTick = MinecraftServer.getServer().getTickCounter(); + long lastAccessed = provider.lastAccessed(x, z); + long diff = currentTick - lastAccessed; + logInfo(" Last accessed: {0, number} Current Tick: {1, number} [{2, number}]", lastAccessed, currentTick, diff); + } + } + + private static void logLoadOnRequest(ChunkProviderServer provider, int x, int z) + { + long currentTick = MinecraftServer.getServer().getTickCounter(); + long lastAccessed = provider.lastAccessed(x, z); + long diff = currentTick - lastAccessed; + logInfo(" Last accessed: {0, number} Current Tick: {1, number} [{2, number}]", lastAccessed, currentTick, diff); + logInfo(" Finding Spawn Point: {0}", provider.worldObj.findingSpawnPoint); + logInfo(" Load chunk on request: {0}", provider.loadChunkOnProvideRequest); + logInfo(" Calling Forge Tick: {0}", MinecraftServer.callingForgeTick); + logInfo(" Load chunk on forge tick: {0}", MinecraftServer.cauldronConfig.loadChunkOnForgeTick.getValue()); + long providerTickDiff = currentTick - provider.initialTick; + if (providerTickDiff <= 100) + { + logInfo(" Current Tick - Initial Tick: {0, number}", providerTickDiff); + } + } + + public static boolean checkBoundingBoxSize(Entity entity, AxisAlignedBB aabb) + { + if (!(entity instanceof EntityLivingBase) || entity instanceof EntityPlayer) return false; // only check living entities that are not players + int logSize = MinecraftServer.cauldronConfig.largeBoundingBoxLogSize.getValue(); + if (logSize <= 0 || !MinecraftServer.cauldronConfig.checkEntityBoundingBoxes.getValue()) return false; + int x = MathHelper.floor_double(aabb.minX); + int x1 = MathHelper.floor_double(aabb.maxX + 1.0D); + int y = MathHelper.floor_double(aabb.minY); + int y1 = MathHelper.floor_double(aabb.maxY + 1.0D); + int z = MathHelper.floor_double(aabb.minZ); + int z1 = MathHelper.floor_double(aabb.maxZ + 1.0D); + + int size = Math.abs(x1-x) * Math.abs(y1-y) * Math.abs(z1-z); + if (size > MinecraftServer.cauldronConfig.largeBoundingBoxLogSize.getValue()) + { + logWarning("Entity being removed for bounding box restrictions"); + logWarning("BB Size: {0} > {1} avg edge: {2}", size, logSize, aabb.getAverageEdgeLength()); + logWarning("Motion: ({0}, {1}, {2})", entity.motionX, entity.motionY, entity.motionZ); + logWarning("Calculated bounding box: {0}", aabb); + logWarning("Entity bounding box: {0}", entity.getBoundingBox()); + logWarning("Entity: {0}", entity); + NBTTagCompound tag = new NBTTagCompound(); + entity.writeToNBT(tag); + logWarning("Entity NBT: {0}", tag); + logStack(); + entity.setDead(); + return true; + } + return false; + } + + public static boolean checkEntitySpeed(Entity entity, double x, double y, double z) + { + int maxSpeed = MinecraftServer.cauldronConfig.entityMaxSpeed.getValue(); + if (maxSpeed > 0 && MinecraftServer.cauldronConfig.checkEntityMaxSpeeds.getValue()) + { + double distance = x * x + z * z; + if (distance > maxSpeed) + { + if (MinecraftServer.cauldronConfig.logEntitySpeedRemoval.getValue()) + { + logInfo("Speed violation: {0} was over {1} - Removing Entity: {2}", distance, maxSpeed, entity); + if (entity instanceof EntityLivingBase) + { + EntityLivingBase livingBase = (EntityLivingBase)entity; + logInfo("Entity Motion: ({0}, {1}, {2}) Move Strafing: {3} Move Forward: {4}", entity.motionX, entity.motionY, entity.motionZ, livingBase.moveStrafing, livingBase.moveForward); + } + + if (MinecraftServer.cauldronConfig.logWithStackTraces.getValue()) + { + logInfo("Move offset: ({0}, {1}, {2})", x, y, z); + logInfo("Motion: ({0}, {1}, {2})", entity.motionX, entity.motionY, entity.motionZ); + logInfo("Entity: {0}", entity); + NBTTagCompound tag = new NBTTagCompound(); + entity.writeToNBT(tag); + logInfo("Entity NBT: {0}", tag); + logStack(); + } + } + if (entity instanceof EntityPlayer) // Skip killing players + { + entity.motionX = 0; + entity.motionY = 0; + entity.motionZ = 0; + return false; + } + // Remove the entity; + entity.isDead = true; + return false; + } + } + return true; + } + + public static void logEntitySize(World world, Entity entity, List list) + { + if (!MinecraftServer.cauldronConfig.logEntityCollisionChecks.getValue()) return; + long largeCountLogSize = MinecraftServer.cauldronConfig.largeCollisionLogSize.getValue(); + if (largeCountLogSize > 0 && world.entitiesTicked > largeCountLogSize) + { + logWarning("Entity size > {0, number} at: {1}", largeCountLogSize, entity); + } + if (list == null) return; + long largeCollisionLogSize = MinecraftServer.cauldronConfig.largeCollisionLogSize.getValue(); + if (largeCollisionLogSize > 0 && + (MinecraftServer.getServer().getTickCounter() % 10) == 0 && + list.size() >= largeCollisionLogSize) + { + CauldronHooks.CollisionWarning warning = new CauldronHooks.CollisionWarning(world, entity); + if (recentWarnings.contains(warning)) + { + long lastWarned = recentWarnings.get(warning); + if ((MinecraftServer.getSystemTimeMillis() - lastWarned) < 30000) return; + } + recentWarnings.put(warning, System.currentTimeMillis()); + logWarning("Entity collision > {0, number} at: {1}", largeCollisionLogSize, entity); + } + } + + private static class CollisionWarning + { + public ChunkCoordinates chunkCoords; + public int dimensionId; + + public CollisionWarning(World world, Entity entity) + { + this.dimensionId = world.provider.dimensionId; + this.chunkCoords = new ChunkCoordinates(entity.chunkCoordX, entity.chunkCoordY, entity.chunkCoordZ); + } + + @Override + public boolean equals(Object otherObj) + { + if (!(otherObj instanceof CollisionWarning) || (otherObj == null)) return false; + CollisionWarning other = (CollisionWarning) otherObj; + return (other.dimensionId == this.dimensionId) && other.chunkCoords.equals(this.chunkCoords); + } + + @Override + public int hashCode() + { + return chunkCoords.hashCode() + dimensionId; + } + } + + public static boolean canTileEntityTick(TileEntity tileEntity, World world) + { + if (tileEntity == null || world.tileentityConfig == null) return false; + if (MinecraftServer.tileEntityConfig.skipTileEntityTicks.getValue()) + { + TileEntityCache teCache = tileEntityCache.get(tileEntity.getClass()); + if (teCache == null) + { + String teConfigPath = tileEntity.getClass().getName().replace(".", "-"); + teConfigPath = teConfigPath.replaceAll("[^A-Za-z0-9\\-]", ""); // Fix up odd class names to prevent YAML errors + teCache = new TileEntityCache(tileEntity.getClass(), world.getWorldInfo().getWorldName().toLowerCase(), teConfigPath, world.tileentityConfig.getBoolean(teConfigPath + ".tick-no-players", false), world.tileentityConfig.getInt(teConfigPath + ".tick-interval", 1)); + tileEntityCache.put(tileEntity.getClass(), teCache); + } + + // Tick with no players near? + if (!teCache.tickNoPlayers && !world.isActiveBlockCoord(tileEntity.xCoord, tileEntity.zCoord)) + { + return false; + } + + // Skip tick interval + if (teCache.tickInterval > 0 && (world.getWorldInfo().getWorldTotalTime() % teCache.tickInterval == 0L)) + { + return true; + } + return false; + } + return true; + } + + public static boolean canUpdate(TileEntity tileEntity) + { + if (tileEntity == null || !tileEntity.canUpdate() || MinecraftServer.bannedTileEntityUpdates.contains(tileEntity.getClass())) return false; // quick exit + return true; + } + + public static void writeChunks(File file, boolean logAll) + { + try + { + if (file.getParentFile() != null) + { + file.getParentFile().mkdirs(); + } + + FileWriter fileWriter = new FileWriter(file); + JsonWriter writer = new JsonWriter(fileWriter); + writer.setIndent(" "); + writer.beginArray(); + + for (net.minecraft.world.WorldServer world : MinecraftServer.getServer().worlds) + { + writer.beginObject(); + writer.name("name").value(world.getWorld().getName()); + writer.name("dimensionId").value(world.provider.dimensionId); + writer.name("players").value(world.playerEntities.size()); + writer.name("loadedChunks").value(world.theChunkProviderServer.loadedChunkHashMap.size()); + writer.name("activeChunks").value(world.activeChunkSet.size()); + writer.name("entities").value(world.loadedEntityList.size()); + writer.name("tiles").value(world.loadedTileEntityList.size()); + + TObjectIntHashMap chunkEntityCounts = new TObjectIntHashMap(); + TObjectIntHashMap classEntityCounts = new TObjectIntHashMap(); + TObjectIntHashMap entityCollisionCounts = new TObjectIntHashMap(); + Set collidingCoords = new HashSet(); + for (int i = 0; i < world.loadedEntityList.size(); i++) + { + Entity entity = (Entity) world.loadedEntityList.get(i); + ChunkCoordIntPair chunkCoords = new ChunkCoordIntPair((int) entity.posX >> 4, (int) entity.posZ >> 4); + chunkEntityCounts.adjustOrPutValue(chunkCoords, 1, 1); + classEntityCounts.adjustOrPutValue(entity.getClass(), 1, 1); + if ((entity.boundingBox != null) && logAll) + { + ChunkCoordinates coords = new ChunkCoordinates((int)Math.floor(entity.posX), (int)Math.floor(entity.posY), (int)Math.floor(entity.posZ)); + if (!collidingCoords.contains(coords)) + { + collidingCoords.add(coords); + int size = entity.worldObj.getEntitiesWithinAABBExcludingEntity(entity, entity.boundingBox.expand(1, 1, 1)).size(); + if (size < 5) + { + continue; + } + entityCollisionCounts.put(entity, size); + } + } + } + + TObjectIntHashMap chunkTileCounts = new TObjectIntHashMap(); + TObjectIntHashMap classTileCounts = new TObjectIntHashMap(); + writer.name("tiles").beginArray(); + for (int i = 0; i < world.loadedTileEntityList.size(); i++) + { + TileEntity tile = (TileEntity) world.loadedTileEntityList.get(i); + if (logAll) + { + writer.beginObject(); + writer.name("type").value(tile.getClass().toString()); + writer.name("x").value(tile.xCoord); + writer.name("y").value(tile.yCoord); + writer.name("z").value(tile.zCoord); + writer.name("isInvalid").value(tile.isInvalid()); + writer.name("canUpdate").value(tile.canUpdate()); + writer.name("block").value("" + tile.getBlockType()); + writer.endObject(); + } + ChunkCoordIntPair chunkCoords = new ChunkCoordIntPair(tile.xCoord >> 4, tile.zCoord >> 4); + chunkTileCounts.adjustOrPutValue(chunkCoords, 1, 1); + classTileCounts.adjustOrPutValue(tile.getClass(), 1, 1); + } + writer.endArray(); + + if (logAll) + { + writeChunkCounts(writer, "topEntityColliders", entityCollisionCounts, 20); + } + writeChunkCounts(writer, "entitiesByClass", classEntityCounts); + writeChunkCounts(writer, "entitiesByChunk", chunkEntityCounts); + + writeChunkCounts(writer, "tilesByClass", classTileCounts); + writeChunkCounts(writer, "tilesByChunk", chunkTileCounts); + + writer.endObject(); // Dimension + } + writer.endArray(); // Dimensions + writer.close(); + fileWriter.close(); + } + catch (Throwable throwable) + { + MinecraftServer.getServer().logSevere("Could not save chunk info report to " + file); + } + } + + private static void writeChunkCounts(JsonWriter writer, String name, final TObjectIntHashMap map) throws IOException + { + writeChunkCounts(writer, name, map, 0); + } + + private static void writeChunkCounts(JsonWriter writer, String name, final TObjectIntHashMap map, int max) throws IOException + { + List sortedCoords = new ArrayList(map.keySet()); + Collections.sort(sortedCoords, new Comparator() + { + @Override + public int compare(T s1, T s2) + { + return map.get(s2) - map.get(s1); + } + }); + + int i = 0; + writer.name(name).beginArray(); + for (T key : sortedCoords) + { + if ((max > 0) && (i++ > max)) + { + break; + } + if (map.get(key) < 5) + { + continue; + } + writer.beginObject(); + writer.name("key").value(key.toString()); + writer.name("count").value(map.get(key)); + writer.endObject(); + } + writer.endArray(); + } + + public static void dumpHeap(File file, boolean live) + { + try + { + if (file.getParentFile() != null) + { + file.getParentFile().mkdirs(); + } + Class clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean"); + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + Object hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", clazz); + Method m = clazz.getMethod("dumpHeap", String.class, boolean.class); + m.invoke(hotspotMBean, file.getPath(), live); + } + catch (Throwable t) + { + logSevere("Could not write heap to {0}", file); + } + } + + public static void enableThreadContentionMonitoring() + { + if (!MinecraftServer.cauldronConfig.enableThreadContentionMonitoring.getValue()) return; + java.lang.management.ThreadMXBean mbean = java.lang.management.ManagementFactory.getThreadMXBean(); + mbean.setThreadContentionMonitoringEnabled(true); + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/CauldronUtils.java b/src/main/java/net/minecraftforge/cauldron/CauldronUtils.java new file mode 100644 index 0000000..e1f737d --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/CauldronUtils.java @@ -0,0 +1,147 @@ +package net.minecraftforge.cauldron; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import cpw.mods.fml.relauncher.FMLRelaunchLog; + +import org.bukkit.inventory.InventoryHolder; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; + +public class CauldronUtils { + private static boolean deobfuscated = false; + + public static boolean isOverridingUpdateEntity(Class c) + { + Class clazz = null; + String method = deobfuscatedEnvironment() ? "updateEntity" : "func_145845_h"; // updateEntity + try + { + clazz = c.getMethod(method).getDeclaringClass(); + } + catch (Throwable e) + { + //e.printStackTrace(); no need for spam + } + + return clazz != TileEntity.class; + } + + public static boolean canTileEntityUpdate(Class c) + { + boolean canUpdate = false; + try + { + Constructor ctor = c.getConstructor(); + TileEntity te = ctor.newInstance(); + canUpdate = te.canUpdate(); + } + catch (Throwable e) + { + // ignore + } + return canUpdate; + } + + public static void dumpAndSortClassList(List> classList) + { + List sortedClassList = new ArrayList(); + for (Class clazz : classList) + { + sortedClassList.add(clazz.getName()); + } + Collections.sort(sortedClassList); + if (MinecraftServer.tileEntityConfig.enableTECanUpdateWarning.getValue()) + { + for (String aSortedClassList : sortedClassList) { + MinecraftServer.getServer().logInfo("Detected TE " + aSortedClassList + " with canUpdate set to true and no updateEntity override!. This is NOT good, please report to mod author as this can hurt performance."); + } + } + } + + public static boolean migrateWorlds(String worldType, String oldWorldContainer, String newWorldContainer, String worldName) + { + boolean result = true; + File newWorld = new File(new File(newWorldContainer), worldName); + File oldWorld = new File(new File(oldWorldContainer), worldName); + + if ((!newWorld.isDirectory()) && (oldWorld.isDirectory())) + { + MinecraftServer.getServer().logInfo("---- Migration of old " + worldType + " folder required ----"); + MinecraftServer.getServer().logInfo("Cauldron has moved back to using the Forge World structure, your " + worldType + " folder will be moved to a new location in order to operate correctly."); + MinecraftServer.getServer().logInfo("We will move this folder for you, but it will mean that you need to move it back should you wish to stop using Cauldron in the future."); + MinecraftServer.getServer().logInfo("Attempting to move " + oldWorld + " to " + newWorld + "..."); + + if (newWorld.exists()) + { + MinecraftServer.getServer().logSevere("A file or folder already exists at " + newWorld + "!"); + MinecraftServer.getServer().logInfo("---- Migration of old " + worldType + " folder failed ----"); + result = false; + } + else if (newWorld.getParentFile().mkdirs() || newWorld.getParentFile().exists()) + { + MinecraftServer.getServer().logInfo("Success! To restore " + worldType + " in the future, simply move " + newWorld + " to " + oldWorld); + + // Migrate world data + try + { + com.google.common.io.Files.move(oldWorld, newWorld); + } + catch (IOException exception) + { + MinecraftServer.getServer().logSevere("Unable to move world data."); + exception.printStackTrace(); + result = false; + } + try + { + com.google.common.io.Files.copy(new File(oldWorld.getParent(), "level.dat"), new File(newWorld, "level.dat")); + } + catch (IOException exception) + { + MinecraftServer.getServer().logSevere("Unable to migrate world level.dat."); + } + + MinecraftServer.getServer().logInfo("---- Migration of old " + worldType + " folder complete ----"); + } + else result = false; + } + return result; + } + + public static InventoryHolder getOwner(TileEntity tileentity) + { + org.bukkit.block.BlockState state = tileentity.worldObj.getWorld().getBlockAt(tileentity.xCoord, tileentity.yCoord, tileentity.zCoord).getState(); + + if (state instanceof InventoryHolder) + { + return (InventoryHolder) state; + } + + return null; + } + + public static boolean deobfuscatedEnvironment() + { + try + { + // Are we in a 'decompiled' environment? + byte[] bs = ((net.minecraft.launchwrapper.LaunchClassLoader)CauldronUtils.class.getClassLoader()).getClassBytes("net.minecraft.world.World"); + if (bs != null) + { + //FMLRelaunchLog.info("Managed to load a deobfuscated Minecraft name- we are in a deobfuscated environment. Skipping runtime deobfuscation"); + deobfuscated = true; + } + } + catch (IOException e1) + { + } + return deobfuscated; + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/CompatibilityMarker.java b/src/main/java/net/minecraftforge/cauldron/CompatibilityMarker.java new file mode 100644 index 0000000..fc14ba8 --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/CompatibilityMarker.java @@ -0,0 +1,5 @@ +package net.minecraftforge.cauldron; + +public class CompatibilityMarker { + +} diff --git a/src/main/java/net/minecraftforge/cauldron/TileEntityCache.java b/src/main/java/net/minecraftforge/cauldron/TileEntityCache.java new file mode 100644 index 0000000..6627cc2 --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/TileEntityCache.java @@ -0,0 +1,21 @@ +package net.minecraftforge.cauldron; + +import net.minecraft.tileentity.TileEntity; + +public class TileEntityCache { + + public Class tileEntityClass; + public boolean tickNoPlayers = false; + public int tickInterval = 1; + public String configPath; + public String worldName; + + public TileEntityCache(Class tileEntityClass, String worldName, String configPath, boolean tickNoPlayers, int tickInterval) + { + this.tileEntityClass = tileEntityClass; + this.worldName = worldName; + this.tickNoPlayers = tickNoPlayers; + this.tickInterval = tickInterval; + this.configPath = configPath; + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/VersionInfo.java b/src/main/java/net/minecraftforge/cauldron/VersionInfo.java new file mode 100644 index 0000000..63f7277 --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/VersionInfo.java @@ -0,0 +1,109 @@ +package net.minecraftforge.cauldron; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import argo.jdom.JdomParser; +import argo.jdom.JsonNode; +import argo.jdom.JsonRootNode; + +import com.google.common.base.Charsets; +import com.google.common.base.Splitter; +import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import com.google.common.io.ByteStreams; +import com.google.common.io.Files; +import com.google.common.io.OutputSupplier; + +public class VersionInfo { + public static final VersionInfo INSTANCE = new VersionInfo(); + public final JsonRootNode versionData; + + public VersionInfo() + { + InputStream installProfile = getClass().getResourceAsStream("/cauldron_libs.json"); + JdomParser parser = new JdomParser(); + + try + { + versionData = parser.parse(new InputStreamReader(installProfile, Charsets.UTF_8)); + } + catch (Exception e) + { + throw Throwables.propagate(e); + } + } + + public static String getProfileName() + { + return INSTANCE.versionData.getStringValue("install","profileName"); + } + + public static String getVersionTarget() + { + return INSTANCE.versionData.getStringValue("install","target"); + } + public static File getLibraryPath(File root) + { + String path = INSTANCE.versionData.getStringValue("install","path"); + String[] split = Iterables.toArray(Splitter.on(':').omitEmptyStrings().split(path), String.class); + File dest = root; + Iterable subSplit = Splitter.on('.').omitEmptyStrings().split(split[0]); + for (String part : subSplit) + { + dest = new File(dest, part); + } + dest = new File(new File(dest, split[1]), split[2]); + String fileName = split[1]+"-"+split[2]+".jar"; + return new File(dest,fileName); + } + + public static String getVersion() + { + return INSTANCE.versionData.getStringValue("install","version"); + } + + public static String getWelcomeMessage() + { + return INSTANCE.versionData.getStringValue("install","welcome"); + } + + public static String getLogoFileName() + { + return INSTANCE.versionData.getStringValue("install","logo"); + } + + public static JsonNode getVersionInfo() + { + return INSTANCE.versionData.getNode("versionInfo"); + } + + public static File getMinecraftFile(File path) + { + return new File(new File(path, getMinecraftVersion()),getMinecraftVersion()+".jar"); + } + public static String getContainedFile() + { + return INSTANCE.versionData.getStringValue("install","filePath"); + } + public static void extractFile(File path) throws IOException + { + INSTANCE.doFileExtract(path); + } + + private void doFileExtract(File path) throws IOException + { + InputStream inputStream = getClass().getResourceAsStream("/"+getContainedFile()); + OutputSupplier outputSupplier = Files.newOutputStreamSupplier(path); + System.out.println("doFileExtract path = " + path.getAbsolutePath() + ", inputStream = " + inputStream + ", outputSupplier = " + outputSupplier); + ByteStreams.copy(inputStream, outputSupplier); + } + + public static String getMinecraftVersion() + { + return INSTANCE.versionData.getStringValue("install","minecraft"); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/cauldron/api/Cauldron.java b/src/main/java/net/minecraftforge/cauldron/api/Cauldron.java new file mode 100644 index 0000000..7f8f63a --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/api/Cauldron.java @@ -0,0 +1,29 @@ +package net.minecraftforge.cauldron.api; + +import net.minecraftforge.cauldron.api.inventory.BukkitOreDictionary; + +/** + * Represents the Bukkit plugin interface to Cauldron, for version and singleton handling + */ +public class Cauldron { + private static CauldronApi instance; + public static void setInterface(CauldronApi cauldron) { + if (instance != null) { + throw new IllegalStateException(); + } + instance = cauldron; + } + + /** + * Gets the current CauldronApi singleton + * + * @return current instance of CauldronApi. will always be present. + */ + public static CauldronApi getInterface() { + return instance; + } + + public static BukkitOreDictionary getOreDictionary() { + return instance.getOreDictionary(); + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/api/CauldronApi.java b/src/main/java/net/minecraftforge/cauldron/api/CauldronApi.java new file mode 100644 index 0000000..f629ba5 --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/api/CauldronApi.java @@ -0,0 +1,22 @@ +package net.minecraftforge.cauldron.api; + +import net.minecraftforge.cauldron.api.inventory.BukkitOreDictionary; + +/** + * Represents the Bukkit plugin interface to Forge features. + */ +public interface CauldronApi { + /** + * Get the ore dictionary interface. + * + * @return ore dictionary interface + */ + public BukkitOreDictionary getOreDictionary(); + + /** + * Get the fishing interface. + * + * @return the fishing interface + */ + public Fishing getFishingInterface(); +} diff --git a/src/main/java/net/minecraftforge/cauldron/api/Fishing.java b/src/main/java/net/minecraftforge/cauldron/api/Fishing.java new file mode 100644 index 0000000..04aa7d8 --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/api/Fishing.java @@ -0,0 +1,72 @@ +package net.minecraftforge.cauldron.api; + +import com.google.common.base.Predicate; +import org.bukkit.inventory.ItemStack; + +import java.util.Random; + +/** + * Bukkit interface to Forge's FishingHooks class. + */ +public interface Fishing { + + /** + * Add a WeightedRandomFishable to the 'fish' results table. + * + * @param fish fishable item + */ + public void addFish(WeightedRandomFishable fish); + + /** + * Add a WeightedRandomFishable to the 'junk' results table. + * + * @param fish fishable item + */ + public void addJunk(WeightedRandomFishable fish); + + /** + * Add a WeightedRandomFishable to the 'treasure' results table. + * + * @param fish fishable item + */ + public void addTreasure(WeightedRandomFishable fish); + + /** + * Remove WeightedRandomFishables from the 'fish' results table. + * Modifications to the Fishable objects will not be kept. + * + * @param test a Predicate giving the removal condition + */ + public void removeMatchingFish(Predicate test); + + /** + * Remove WeightedRandomFishables from the 'junk' results table. + * Modifications to the Fishable objects will not be kept. + * + * @param test a Predicate giving the removal condition + */ + public void removeMatchingJunk(Predicate test); + + /** + * Remove WeightedRandomFishables from the 'treasure' results table. + * Modifications to the Fishable objects will not be kept. + * + * @param test a Predicate giving the removal condition + */ + public void removeMatchingTreasure(Predicate test); + + /** + * Get the item pulled up from a simulated fishing attempt. + * + * @param rand the Random instance to use + * @param baseChance roughly, a percentage chance (0-1) to + * get a fish + * @param fishingLuckEnchantmentLevel the value of {@link org.bukkit.enchantments.Enchantment#LUCK} + * on the fishing rod + * @param fishingSpeedEnchantmentLevel the value of {@link org.bukkit.enchantments.Enchantment#LURE} + * on the fishing rod + * @return the item fished + */ + public ItemStack getRandomFishable(Random rand, float baseChance, int fishingLuckEnchantmentLevel, int fishingSpeedEnchantmentLevel); + +} diff --git a/src/main/java/net/minecraftforge/cauldron/api/WeightedRandomFishable.java b/src/main/java/net/minecraftforge/cauldron/api/WeightedRandomFishable.java new file mode 100644 index 0000000..9441d6b --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/api/WeightedRandomFishable.java @@ -0,0 +1,65 @@ +package net.minecraftforge.cauldron.api; + +import org.bukkit.inventory.ItemStack; + +public class WeightedRandomFishable { + private ItemStack itemStack; + private int weight; + private boolean hasRandomEnchantments; + private float damageFraction; + + public WeightedRandomFishable(ItemStack itemStack, int weight) { + this.itemStack = itemStack; + this.weight = weight; + } + + /** + * Setting this value results in fished items having random damage when fished. The damage is in a triangular + * random distribution (think about rolling 2 dice), with the wide end at 100% and + * the narrow end at (100% - damageFraction). + * + * For use in a chaining constructor. + * + * @param damageFraction low boundary for random distribution + * @return this WeightedRandomFishable, for chaining + */ + public final WeightedRandomFishable withDamageFraction(float damageFraction) { + damageFraction = damageFraction; + return this; + } + + /** + * Mark this WeightedRandomFishable as receiving random enchantments. + * @return this WeightedRandomFishable, for chaining + */ + public final WeightedRandomFishable withRandomEnchantments() { + hasRandomEnchantments = true; + return this; + } + + /** + * Set whether this WeightedRandomFishable receives random enchantments. + * (Use this method if loading from another source, such as a config file.) + * @return this WeightedRandomFishable, for chaining + */ + public final WeightedRandomFishable withRandomEnchantments(boolean hasEnchants) { + hasRandomEnchantments = hasEnchants; + return this; + } + + public ItemStack getItemStack() { + return itemStack; + } + + public int getWeight() { + return weight; + } + + public boolean hasRandomEnchantments() { + return hasRandomEnchantments; + } + + public float getDamageFraction() { + return damageFraction; + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/api/inventory/BukkitOreDictionary.java b/src/main/java/net/minecraftforge/cauldron/api/inventory/BukkitOreDictionary.java new file mode 100644 index 0000000..7071181 --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/api/inventory/BukkitOreDictionary.java @@ -0,0 +1,78 @@ +package net.minecraftforge.cauldron.api.inventory; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +/** + * The Forge Ore Dictionary provides a way for multiple items to share a common + * identifier - for instance, "ingotCopper" - and be used interchangeably in + * crafting recipes. + *

+ * This class provides a read-only interface to the ore dictionary using Bukkit + * classes, instead of NMS classes. + */ +public interface BukkitOreDictionary { + /** + * If an item's damage is a wildcard, this will be returned for the item + * damage. + */ + public static final int WILDCARD_VALUE = Short.MAX_VALUE; + + /** + * Check the OreDictionaryEntry for a given name. The name can be later + * retrieved using {@link #getOreName(OreDictionaryEntry)}, and having an + * entry is required to call {@link #getDefinitions(OreDictionaryEntry)}. + * + * @param name name in the ore dictionary + * @return ore dictionary entry, or null if name is not present + */ + public OreDictionaryEntry getOreEntry(String name); + + /** + * Get the string name defined for the given OreDictionaryEntry. + *

+ * This is called by {@link OreDictionaryEntry#getName()}. + * + * @param entry ore dictionary entry + * @return ore dictionary name + */ + public String getOreName(OreDictionaryEntry entry); + + /** + * Get all of the OreDictionaryEntry objects registered to the given + * ItemStack. + * + * @param itemStack itemstack to check - amount is ignored + * @return immutable list of ore dictionary entries + */ + public List getOreEntries(ItemStack itemStack); + + /** + * Get all of the OreDictionaryEntry objects registered to the given + * material and no item damage. + * + * @param material material to check + * @return immutable list of ore dictionary entries + */ + public List getOreEntries(Material material); + + /** + * Get all of the ItemStacks registered to the given ore dictionary entry. + *

+ * Quantity should be ignored. + * + * @param entry ore dictionary entry + * @return immutable list of itemstacks + */ + public List getDefinitions(OreDictionaryEntry entry); + + /** + * Get all ore names in the dictionary. The returned list may contain + * multiple null values or duplicates. + * + * @return all ore dictionary names + */ + public List getAllOreNames(); +} diff --git a/src/main/java/net/minecraftforge/cauldron/api/inventory/OreDictionaryEntry.java b/src/main/java/net/minecraftforge/cauldron/api/inventory/OreDictionaryEntry.java new file mode 100644 index 0000000..1419b8b --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/api/inventory/OreDictionaryEntry.java @@ -0,0 +1,62 @@ +package net.minecraftforge.cauldron.api.inventory; + +import net.minecraftforge.cauldron.api.Cauldron; + +import java.util.ArrayList; +import java.util.List; + +/** + * An OreDictionaryEntry is an opaque reference to an entry in the Forge Ore + * Dictionary. This class has reference equality. + */ +public class OreDictionaryEntry { + private static List oreDictionaryEntries = new ArrayList(); + + /** + * Get an OreDictionaryEntry instance, using an instance cache to preserve + * reference equality. + * + * @param id opaque ore dictionary id + * @return object wrapper around id + */ + public static OreDictionaryEntry valueOf(int id) { + if (id < 0) throw new IllegalArgumentException("ore dictionary IDs are not negative"); + + while (oreDictionaryEntries.size() < id + 1) { + oreDictionaryEntries.add(new OreDictionaryEntry(oreDictionaryEntries.size())); + } + return oreDictionaryEntries.get(id); + } + + private int id; + private OreDictionaryEntry(int id) { + this.id = id; + } + + /** + * Get the opaque ID of this ore-dictionary entry. + * + * Plugins should not inspect the results of this call. Results may not be + * the same across multiple server startups. + * + * @return Opaque id number. + */ + public int getId() { + return id; + } + + /** + * Request the Ore Dictionary for the string identifier of this entry. + * + * @return ore dictionary string + * @see net.minecraftforge.cauldron.api.inventory.BukkitOreDictionary#getOreName(OreDictionaryEntry) + */ + public String getName() { + return Cauldron.getOreDictionary().getOreName(this); + } + + @Override + public String toString() { + return String.format("OreDictionary{id=%d,name=%s}", id, getName()); + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/apiimpl/CauldronPluginInterface.java b/src/main/java/net/minecraftforge/cauldron/apiimpl/CauldronPluginInterface.java new file mode 100644 index 0000000..77a9c4a --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/apiimpl/CauldronPluginInterface.java @@ -0,0 +1,29 @@ +package net.minecraftforge.cauldron.apiimpl; + +import net.minecraftforge.cauldron.api.Cauldron; +import net.minecraftforge.cauldron.api.CauldronApi; +import net.minecraftforge.cauldron.api.Fishing; +import net.minecraftforge.cauldron.api.inventory.BukkitOreDictionary; +import net.minecraftforge.cauldron.apiimpl.inventory.OreDictionaryInterface; +import org.bukkit.Bukkit; +import org.bukkit.plugin.ServicePriority; + +public class CauldronPluginInterface implements CauldronApi { + private BukkitOreDictionary oreDictionary = new OreDictionaryInterface(); + private Fishing fishingInterface = new FishingInterface(); + + public void install() { + Cauldron.setInterface(this); + Bukkit.getServicesManager().register(CauldronApi.class, this, null, ServicePriority.Highest); + } + + @Override + public BukkitOreDictionary getOreDictionary() { + return oreDictionary; + } + + @Override + public Fishing getFishingInterface() { + return fishingInterface; + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/apiimpl/FishingInterface.java b/src/main/java/net/minecraftforge/cauldron/apiimpl/FishingInterface.java new file mode 100644 index 0000000..bf9b9ed --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/apiimpl/FishingInterface.java @@ -0,0 +1,81 @@ +package net.minecraftforge.cauldron.apiimpl; + +import com.google.common.base.Predicate; +import net.minecraftforge.cauldron.api.Fishing; +import net.minecraftforge.cauldron.api.WeightedRandomFishable; +import net.minecraftforge.common.FishingHooks; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +import java.util.Random; + +public class FishingInterface implements Fishing { + private static net.minecraft.util.WeightedRandomFishable toNms(WeightedRandomFishable bukkit) { + net.minecraft.util.WeightedRandomFishable ret = + new net.minecraft.util.WeightedRandomFishable( + CraftItemStack.asNMSCopy(bukkit.getItemStack()), + bukkit.getWeight()).func_150709_a(bukkit.getDamageFraction()); + if (bukkit.hasRandomEnchantments()) { + ret.func_150707_a(); + } + return ret; + } + + private static WeightedRandomFishable toBukkit(net.minecraft.util.WeightedRandomFishable nms) { + return new WeightedRandomFishable(CraftItemStack.asBukkitCopy(nms.field_150711_b), nms.itemWeight) + .withDamageFraction(nms.field_150712_c) + .withRandomEnchantments(nms.field_150710_d); + } + + private static class PredicateProxy implements Predicate { + private Predicate bukkitPredicate; + + public PredicateProxy(Predicate predicate) { + this.bukkitPredicate = predicate; + } + + @Override + public boolean apply(net.minecraft.util.WeightedRandomFishable input) { + return bukkitPredicate.apply(toBukkit(input)); + } + } + + private static PredicateProxy toNms(Predicate predicate) { + return new PredicateProxy(predicate); + } + + @Override + public void addFish(WeightedRandomFishable fish) { + FishingHooks.addFish(toNms(fish)); + } + + @Override + public void addJunk(WeightedRandomFishable fish) { + FishingHooks.addJunk(toNms(fish)); + } + + @Override + public void addTreasure(WeightedRandomFishable fish) { + FishingHooks.addTreasure(toNms(fish)); + } + + @Override + public void removeMatchingFish(Predicate test) { + FishingHooks.removeFish(toNms(test)); + } + + @Override + public void removeMatchingJunk(Predicate test) { + FishingHooks.removeJunk(toNms(test)); + } + + @Override + public void removeMatchingTreasure(Predicate test) { + FishingHooks.removeTreasure(toNms(test)); + } + + @Override + public ItemStack getRandomFishable(Random rand, float baseChance, int fishingLuckEnchantmentLevel, int fishingSpeedEnchantmentLevel) { + return CraftItemStack.asCraftMirror(FishingHooks.getRandomFishable(rand, baseChance, fishingLuckEnchantmentLevel, fishingSpeedEnchantmentLevel)); + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/apiimpl/inventory/OreDictionaryInterface.java b/src/main/java/net/minecraftforge/cauldron/apiimpl/inventory/OreDictionaryInterface.java new file mode 100644 index 0000000..b37bcec --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/apiimpl/inventory/OreDictionaryInterface.java @@ -0,0 +1,83 @@ +package net.minecraftforge.cauldron.apiimpl.inventory; + +import com.google.common.collect.ImmutableList; +import net.minecraftforge.cauldron.api.inventory.BukkitOreDictionary; +import net.minecraftforge.cauldron.api.inventory.OreDictionaryEntry; +import net.minecraftforge.oredict.OreDictionary; +import org.bukkit.Material; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OreDictionaryInterface implements BukkitOreDictionary { + private Map normalizedToCanonicalMap = null; + + private void initializeMap() { + normalizedToCanonicalMap = new HashMap(); + + for (String str : getAllOreNames()) { + if (str == null || str.isEmpty()) { + continue; + } + normalizedToCanonicalMap.put(Material.normalizeName(str), str); + } + } + + @Override + public OreDictionaryEntry getOreEntry(String name) { + if (normalizedToCanonicalMap == null) { + initializeMap(); + } + + String canonical = normalizedToCanonicalMap.get(Material.normalizeName(name)); + if (canonical == null) { + return null; + } + + return OreDictionaryEntry.valueOf(OreDictionary.getOreID(canonical)); + } + + @Override + public List getOreEntries(ItemStack itemStack) { + int[] ids = OreDictionary.getOreIDs(CraftItemStack.asNMSCopy(itemStack)); + + ImmutableList.Builder builder = ImmutableList.builder(); + for (int id : ids) { + builder.add(OreDictionaryEntry.valueOf(id)); + } + + return builder.build(); + } + + @Override + public List getOreEntries(Material material) { + return getOreEntries(new ItemStack(material)); + } + + @Override + public String getOreName(OreDictionaryEntry entry) { + return OreDictionary.getOreName(entry.getId()); + } + + @Override + public List getDefinitions(OreDictionaryEntry entry) { + @SuppressWarnings("deprecation") + List items = OreDictionary.getOres(entry.getId()); + + ImmutableList.Builder builder = ImmutableList.builder(); + for (net.minecraft.item.ItemStack nmsItem : items) { + builder.add(CraftItemStack.asCraftMirror(nmsItem)); + } + + return builder.build(); + } + + @Override + public List getAllOreNames() { + return Arrays.asList(OreDictionary.getOreNames()); + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/block/CraftCustomContainer.java b/src/main/java/net/minecraftforge/cauldron/block/CraftCustomContainer.java new file mode 100644 index 0000000..24d979b --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/block/CraftCustomContainer.java @@ -0,0 +1,27 @@ +package net.minecraftforge.cauldron.block; + +import net.minecraft.inventory.IInventory; + +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class CraftCustomContainer extends CraftBlockState implements InventoryHolder { + private final CraftWorld world; + private final net.minecraft.inventory.IInventory container; + + public CraftCustomContainer(Block block) { + super(block); + world = (CraftWorld) block.getWorld(); + container = (IInventory)world.getTileEntityAt(getX(), getY(), getZ()); + } + + @Override + public Inventory getInventory() { + CraftInventory inventory = new CraftInventory(container); + return inventory; + } +} diff --git a/src/main/java/net/minecraftforge/cauldron/command/CauldronCommand.java b/src/main/java/net/minecraftforge/cauldron/command/CauldronCommand.java new file mode 100644 index 0000000..b29bdf2 --- /dev/null +++ b/src/main/java/net/minecraftforge/cauldron/command/CauldronCommand.java @@ -0,0 +1,258 @@ +package net.minecraftforge.cauldron.command; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.cauldron.CauldronHooks; +import net.minecraftforge.cauldron.configuration.BoolSetting; +import net.minecraftforge.cauldron.configuration.IntSetting; +import net.minecraftforge.cauldron.configuration.Setting; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.math.NumberUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.util.StringUtil; + + +import com.google.common.collect.ImmutableList; + +public class CauldronCommand extends Command +{ + private static final List COMMANDS = ImmutableList.of("get", "set", "tick-interval", "save", "reload", "chunks", "heap"); + private static final List CHUNK_COMMANDS = ImmutableList.of("print", "dump"); + + public CauldronCommand() + { + super("cauldron"); + this.description = "Toggle certain Cauldron options"; + + this.usageMessage = "/cauldron [" + StringUtils.join(COMMANDS, '|') + "]