diff --git a/COPYING.txt b/COPYING.txt deleted file mode 100644 index 94a9ed024..000000000 --- a/COPYING.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/COPYING.LESSER.txt b/LICENSE.txt similarity index 100% rename from COPYING.LESSER.txt rename to LICENSE.txt diff --git a/README.md b/README.md index 27b51b7ff..2dd1fcb0a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ Item Scroller ============== -Item Scroller is a tiny mod for Minecraft 1.8.9+, which adds the functionality of moving items in inventory GUIs -by scrolling the mouse wheel over slots with items in them. -This is basically what NEI does/did (and Mouse Tweaks also seems to do), but separated into this tiny mod, -and probably implemented in a different manner. -For more information and the downloads (compiled builds), see http://minecraft.curseforge.com/projects/item-scroller +Item Scroller is a Minecraft mod that adds various convenience features for moving items +inside inventory GUIs. Examples are scrolling the mouse wheel over slots with items in them +or Shift/Ctrl + click + dragging over slots to move items from them in various ways etc. + +Item scrolling is basically what the old NEI mod did and Mouse Tweaks also does. +This mod has some different drag features compared to Mouse Tweaks, and also some special +villager trading related helper features as well as crafting helper features. + +For more information and downloads of the already compiled builds, +see https://www.curseforge.com/minecraft/mc-mods/item-scroller Compiling ========= diff --git a/build.gradle b/build.gradle index 2ae0a9839..1102c784f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,135 +1,60 @@ - -buildscript { - repositories { - jcenter() - maven { - name = "forge" - url = "http://files.minecraftforge.net/maven" - } - maven { - name = 'sponge' - url = 'http://repo.spongepowered.org/maven' - } - } - - dependencies { - classpath "net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT" - classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT' - } -} - -repositories { - maven { - url 'https://masa.dy.fi/maven' - } +plugins { + id 'fabric-loom' version '1.1-SNAPSHOT' } -apply plugin: 'net.minecraftforge.gradle.liteloader' -apply plugin: 'org.spongepowered.mixin' +sourceCompatibility = JavaVersion.VERSION_17 +targetCompatibility = JavaVersion.VERSION_17 -ext.configFile = file "build.properties" - -configFile.withReader { - def prop = new Properties() - prop.load(it) - project.ext.config = new ConfigSlurper().parse prop +repositories { + maven { url 'https://masa.dy.fi/maven' } + maven { url 'https://maven.terraformersmc.com/releases/' } + // maven { url 'https://maven.quiltmc.org/repository/release/' } } -minecraft { - version = config.minecraft_version - mappings = config.mappings_version - runDir = 'minecraft' - makeObfSourceJar = false - - ext.mod_version = config.mod_version - - if (mod_version.endsWith('-dev')) { - mod_version = mod_version + "." + new Date().format('yyyyMMdd.HHmmss') - } +dependencies { + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.mappings_version}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}" + implementation "com.google.code.findbugs:jsr305:3.0.2" - replaceIn "Reference.java" - replace "@MOD_VERSION@", mod_version -} + modImplementation "fi.dy.masa.malilib:malilib-fabric-${project.minecraft_version_out}:${project.malilib_version}" -dependencies { - deobfCompile "fi.dy.masa.malilib:malilib-" + config.minecraft_version_out + ":" + config.malilib_version + ":deobf" -} + // Fabric API. This is technically optional, but you probably want it anyway. + //modCompile "net.fabricmc.fabric-api:fabric-api:" + project.fabric_version -sourceSets { - main { - ext.refMap = 'mixins.' + config.mod_id + '.refmap.json' - } + modCompileOnly "com.terraformersmc:modmenu:${project.mod_menu_version}" } -group = config.group + config.mod_id // http://maven.apache.org/guides/mini/guide-naming-conventions.html -archivesBaseName = config.mod_id + '-' + config.minecraft_version_out -version = project.minecraft.mod_version +group = project.group + "." + project.mod_id +archivesBaseName = project.mod_file_name + '-' + project.minecraft_version_out +version = project.mod_version -/** - * This section allows you to customise the generated litemod.json file - */ -litemod { - json { - name = config.mod_id - displayName = config.mod_name - mcversion = config.minecraft_version - version = config.mod_version - author = config.author - - // Uncomment any of the following lines and fill in your own details as required - //requiredAPIs = [ 'someapi' ] - //tweakClass = 'name.of.tweaker.here' - dependsOn = [ 'malilib' ] - mixinConfigs = [ 'mixins.' + config.mod_id + '.json' ] - } +if (version.endsWith('-dev')) { + version += "." + new Date().format('yyyyMMdd.HHmmss') } -/** - * This section allows you to customise your generated jar (litemod) file. By - * default it includes the generated litemod.json file, however if you prefer to - * include your own file from resources, simply remove the line below. - */ -jar { - // Remove the "-mc1.12" suffix from the file name - classifier = "" - - // Don't append a 'mod-' filename prefix >_> - baseName = archivesBaseName +processResources { + // Exclude the GIMP image files + exclude '**/*.xcf' + exclude '**/xcf' - from litemod.outputs + // this will ensure that this task is redone when the versions change. + //inputs.property "minecraft_version", project.project.minecraft_version - /* - manifest.mainAttributes ( - 'Built-By': System.properties['user.name'], - 'Created-By': System.properties['java.vm.version'] + " (" + System.properties['java.vm.vendor'] + ")", - 'Implementation-Title': config.mod_id, - 'Implementation-Version': project.version - ) - */ -} + inputs.property "mod_version", project.mod_version -mixin { - defaultObfuscationEnv notch + filesMatching("fabric.mod.json") { + expand "mod_version": project.mod_version + } } -//tasks.withType(Jar)*.baseName = archivesBaseName - -processResources -{ - // this will ensure that this task is redone when the versions change. - inputs.property "mod_version", project.minecraft.mod_version - inputs.property "minecraft_version", project.config.minecraft_version +tasks.withType(JavaCompile).configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" - // replace stuff in mcmod.info, nothing else - from(sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - - // replace version and mcversion - expand 'mod_version': project.minecraft.mod_version, 'minecraft_version': project.config.minecraft_version - } - - // copy everything else, thats not the mcmod.info - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - } + // Minecraft 1.18 (1.18-pre2) upwards uses Java 17. + it.options.release = 17 } diff --git a/build.properties b/build.properties deleted file mode 100644 index 5e6b35dcb..000000000 --- a/build.properties +++ /dev/null @@ -1,12 +0,0 @@ -# Thu Jan 14 08:33:00 EET 2016 -group = fi.dy.masa -mod_id = itemscroller -mod_name = Item Scroller -mod_version = 0.15.0-dev -author = masa - -# Minecraft, Forge and MCP mappings versions -minecraft_version_out = 1.12.0 -minecraft_version = 1.12 -mappings_version = stable_39 -malilib_version = 0.10.0-dev.17 diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 000000000..4f24bdb63 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +org.gradle.jvmargs = -Xmx3G +org.gradle.daemon = false +org.gradle.cache.cleanup = false + +group = fi.dy.masa +mod_id = itemscroller +mod_name = Item Scroller +author = masa +mod_file_name = itemscroller-fabric + +# Current mod version +mod_version = 0.19.2 + +# Required malilib version +malilib_version = 0.15.2 + +# Minecraft, Fabric and mappings versions +minecraft_version_out = 1.19.4 +minecraft_version = 1.19.4 +mappings_version = 1.19.4+build.1 + +fabric_loader_version = 0.14.17 +mod_menu_version = 6.1.0-rc.4 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d3b83982b..29953ea14 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 65041d7b3..b5e74cfbf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Jan 05 21:33:17 EET 2017 +#Mon Aug 22 17:36:22 EDT 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 27309d923..cccdd3d51 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -154,11 +154,19 @@ if $cygwin ; then 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=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 832fdb607..f9553162f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -49,7 +49,6 @@ goto fail @rem Get command-line arguments, handling Windows 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. @@ -60,11 +59,6 @@ set _SKIP=2 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 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..027b233da --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/InitHandler.java b/src/main/java/fi/dy/masa/itemscroller/InitHandler.java index e4da2574d..11a226ff5 100644 --- a/src/main/java/fi/dy/masa/itemscroller/InitHandler.java +++ b/src/main/java/fi/dy/masa/itemscroller/InitHandler.java @@ -6,6 +6,7 @@ import fi.dy.masa.itemscroller.event.WorldLoadListener; import fi.dy.masa.malilib.config.ConfigManager; import fi.dy.masa.malilib.event.InputEventHandler; +import fi.dy.masa.malilib.event.TickHandler; import fi.dy.masa.malilib.event.WorldLoadHandler; import fi.dy.masa.malilib.interfaces.IInitializationHandler; @@ -25,6 +26,8 @@ public void registerModHandlers() WorldLoadHandler.getInstance().registerWorldLoadPreHandler(listener); WorldLoadHandler.getInstance().registerWorldLoadPostHandler(listener); + TickHandler.getInstance().registerClientTickHandler(KeybindCallbacks.getInstance()); + KeybindCallbacks.getInstance().setCallbacks(); } } diff --git a/src/main/java/fi/dy/masa/itemscroller/ItemScroller.java b/src/main/java/fi/dy/masa/itemscroller/ItemScroller.java new file mode 100644 index 000000000..561e6b886 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/ItemScroller.java @@ -0,0 +1,17 @@ +package fi.dy.masa.itemscroller; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import fi.dy.masa.malilib.event.InitializationHandler; +import net.fabricmc.api.ModInitializer; + +public class ItemScroller implements ModInitializer +{ + public static final Logger logger = LogManager.getLogger(Reference.MOD_ID); + + @Override + public void onInitialize() + { + InitializationHandler.getInstance().registerInitializationHandler(new InitHandler()); + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/LiteModItemScroller.java b/src/main/java/fi/dy/masa/itemscroller/LiteModItemScroller.java deleted file mode 100644 index 9feb55eda..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/LiteModItemScroller.java +++ /dev/null @@ -1,57 +0,0 @@ -package fi.dy.masa.itemscroller; - -import java.io.File; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import com.mumfrey.liteloader.Configurable; -import com.mumfrey.liteloader.LiteMod; -import com.mumfrey.liteloader.Tickable; -import com.mumfrey.liteloader.modconfig.ConfigPanel; -import fi.dy.masa.itemscroller.config.ItemScrollerConfigPanel; -import fi.dy.masa.itemscroller.event.KeybindCallbacks; -import fi.dy.masa.malilib.event.InitializationHandler; -import net.minecraft.client.Minecraft; - -public class LiteModItemScroller implements LiteMod, Configurable, Tickable -{ - public static final Logger logger = LogManager.getLogger(Reference.MOD_ID); - - public LiteModItemScroller() - { - } - - @Override - public String getName() - { - return Reference.MOD_NAME; - } - - @Override - public String getVersion() - { - return Reference.MOD_VERSION; - } - - @Override - public Class getConfigPanelClass() - { - return ItemScrollerConfigPanel.class; - } - - @Override - public void init(File configPath) - { - InitializationHandler.getInstance().registerInitializationHandler(new InitHandler()); - } - - @Override - public void upgradeSettings(String version, File configPath, File oldConfigPath) - { - } - - @Override - public void onTick(Minecraft mc, float partialTicks, boolean inGame, boolean clock) - { - KeybindCallbacks.getInstance().onTick(mc); - } -} diff --git a/src/main/java/fi/dy/masa/itemscroller/compat/modmenu/ModMenuImpl.java b/src/main/java/fi/dy/masa/itemscroller/compat/modmenu/ModMenuImpl.java new file mode 100644 index 000000000..855d80ba7 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/compat/modmenu/ModMenuImpl.java @@ -0,0 +1,18 @@ +package fi.dy.masa.itemscroller.compat.modmenu; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import fi.dy.masa.itemscroller.gui.GuiConfigs; + +public class ModMenuImpl implements ModMenuApi +{ + @Override + public ConfigScreenFactory getModConfigScreenFactory() + { + return (screen) -> { + GuiConfigs gui = new GuiConfigs(); + gui.setParent(screen); + return gui; + }; + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java index 8dd2f5208..3962385ff 100644 --- a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java +++ b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java @@ -7,18 +7,19 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import fi.dy.masa.itemscroller.Reference; -import fi.dy.masa.itemscroller.recipes.CraftingHandler; -import fi.dy.masa.itemscroller.recipes.CraftingHandler.SlotRange; +import net.minecraft.client.gui.screen.ingame.CraftingScreen; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.screen.slot.CraftingResultSlot; import fi.dy.masa.malilib.config.ConfigUtils; import fi.dy.masa.malilib.config.IConfigHandler; import fi.dy.masa.malilib.config.IConfigValue; import fi.dy.masa.malilib.config.options.ConfigBoolean; +import fi.dy.masa.malilib.config.options.ConfigInteger; import fi.dy.masa.malilib.util.FileUtils; import fi.dy.masa.malilib.util.JsonUtils; -import net.minecraft.client.gui.inventory.GuiCrafting; -import net.minecraft.client.gui.inventory.GuiInventory; -import net.minecraft.inventory.SlotCrafting; +import fi.dy.masa.itemscroller.Reference; +import fi.dy.masa.itemscroller.recipes.CraftingHandler; +import fi.dy.masa.itemscroller.recipes.CraftingHandler.SlotRange; public class Configs implements IConfigHandler { @@ -29,23 +30,41 @@ public static class Generic public static final ConfigBoolean CARPET_CTRL_Q_CRAFTING = new ConfigBoolean("carpetCtrlQCraftingEnabledOnServer", false, "Set to true if the server is running the Carpet mod,\nand has the ctrlQCrafting option enabled.\nThis just changes which method Item Scroller uses\nfor the Drop key + Shift + Right click crafting."); public static final ConfigBoolean CLIENT_CRAFTING_FIX = new ConfigBoolean("clientCraftingFixOn1.12", true, "Enable updating the crafting recipe output directly on the client side.\nThis fixes the quick/mass crafting and right-click-to-craft-a-stack\nfeatures othrwise being broken in 1.12."); public static final ConfigBoolean CRAFTING_RENDER_RECIPE_ITEMS = new ConfigBoolean("craftingRenderRecipeItems", true, "If enabled, then the recipe items are also rendered\nin the crafting recipe view."); + public static final ConfigBoolean MOD_MAIN_TOGGLE = new ConfigBoolean("modMainToggle", true, "Can disable all the functionality of the entire mod"); + public static final ConfigBoolean MASS_CRAFT_INHIBIT_MID_UPDATES = new ConfigBoolean("massCraftInhibitMidUpdates", true, "Prevent recipe output slot updates in the middle of moving items\nto the crafting grid. This should reduce CPU usage\nbecause of not constantly querying the recipe after every grid change."); + public static final ConfigInteger MASS_CRAFT_INTERVAL = new ConfigInteger("massCraftInterval", 2, 1, 60, "The interval in game ticks the massCraft operation is repeated at"); + public static final ConfigInteger MASS_CRAFT_ITERATIONS = new ConfigInteger("massCraftIterations", 36, 1, 256, "How many massCraft iterations/attempts to do per execution.\nWith unstackable items or a full inventory and \"small recipe\"\nthis will need to be larger, as a shift + click craft to the inventory\nmight only craft 1 or 2 items per operation."); + public static final ConfigBoolean MASS_CRAFT_SWAPS = new ConfigBoolean("massCraftSwapsOnly", false, "Uses a newer method of filling the crafting grid,\nusing only swap slot packets.\n\nNote: Due to only using slot swap packets,\nno partial crafts are possible! And also no\nstack splitting will happen, at all."); + public static final ConfigInteger PACKET_RATE_LIMIT = new ConfigInteger("packetRateLimit", 4, 1, 1024, "The limit of sent emulated slot click packets per game tick,\nif 'rateLimitClickPackets' is enabled"); public static final ConfigBoolean SCROLL_CRAFT_STORE_RECIPES_TO_FILE = new ConfigBoolean("craftingRecipesSaveToFile", true, "If enabled, then the crafting features recipes are saved to a file\ninside minecraft/itemscroller/recipes_worldorservername.nbt.\nThis makes the recipes persistent across game restarts."); public static final ConfigBoolean SCROLL_CRAFT_RECIPE_FILE_GLOBAL = new ConfigBoolean("craftingRecipesSaveFileIsGlobal", false, "If true, then the recipe file is global, instead\n of being saved per-world or server"); + public static final ConfigBoolean RATE_LIMIT_CLICK_PACKETS = new ConfigBoolean("rateLimitClickPackets", false, "This is meant for compatibility with Spigot servers and similar,\nwhich apply rate limits to packets from the client.\nThis queues up the emulated slot click packets and sends\nthem rate limited over time. The limit per game tick can be set in 'packetRateLimit´."); public static final ConfigBoolean REVERSE_SCROLL_DIRECTION_SINGLE = new ConfigBoolean("reverseScrollDirectionSingle", false, "Reverse the scrolling direction for single item mode."); public static final ConfigBoolean REVERSE_SCROLL_DIRECTION_STACKS = new ConfigBoolean("reverseScrollDirectionStacks", false, "Reverse the scrolling direction for full stacks mode."); + public static final ConfigBoolean USE_RECIPE_CACHING = new ConfigBoolean("useRecipeCaching", true, "Enables caching the last used recipe in the crafting\nrecipe output item fetching code. This can help a lot\nwith lowering CPU usage when mass crafting stuff."); public static final ConfigBoolean SLOT_POSITION_AWARE_SCROLL_DIRECTION = new ConfigBoolean("useSlotPositionAwareScrollDirection", false, "When enabled, the item movement direction depends\non the slots' y-position on screen. Might be derpy with more\ncomplex inventories, use with caution!"); - public static final ConfigBoolean VILLAGER_TRADE_LIST_REMEMBER_PAGE = new ConfigBoolean("villagerTradeListRememberPage", true, "Remember and restore the last looked at page/trade when re-opening the GUI"); + public static final ConfigBoolean VILLAGER_TRADE_USE_GLOBAL_FAVORITES = new ConfigBoolean("villagerTradeUseGlobalFavorites", true, "Whether or not global (per-item-type) villager trade\nfavorites should be used."); + public static final ConfigBoolean VILLAGER_TRADE_LIST_REMEMBER_SCROLL = new ConfigBoolean("villagerTradeListRememberScrollPosition", true, "Remember and restore the last scroll position in the\ntrade list when re-opening the GUI"); public static final ImmutableList OPTIONS = ImmutableList.of( CARPET_CTRL_Q_CRAFTING, CLIENT_CRAFTING_FIX, CRAFTING_RENDER_RECIPE_ITEMS, + MASS_CRAFT_INHIBIT_MID_UPDATES, + MASS_CRAFT_INTERVAL, + MASS_CRAFT_ITERATIONS, + MASS_CRAFT_SWAPS, + MOD_MAIN_TOGGLE, + PACKET_RATE_LIMIT, + RATE_LIMIT_CLICK_PACKETS, SCROLL_CRAFT_STORE_RECIPES_TO_FILE, SCROLL_CRAFT_RECIPE_FILE_GLOBAL, REVERSE_SCROLL_DIRECTION_SINGLE, REVERSE_SCROLL_DIRECTION_STACKS, SLOT_POSITION_AWARE_SCROLL_DIRECTION, - VILLAGER_TRADE_LIST_REMEMBER_PAGE + USE_RECIPE_CACHING, + VILLAGER_TRADE_USE_GLOBAL_FAVORITES, + VILLAGER_TRADE_LIST_REMEMBER_SCROLL ); } @@ -62,7 +81,7 @@ public static class Toggles public static final ConfigBoolean SCROLL_VILLAGER = new ConfigBoolean("enableScrollingVillager", true, "Enables special handling for the Villager GUIs.\n(Normally you can't shift click items in them.)\nHold shift and scroll up/down over the trade output slot."); public static final ConfigBoolean SHIFT_DROP_ITEMS = new ConfigBoolean("enableShiftDropItems", true, "Enables dropping all matching items at once by holding\nshift while clicking to drop a stack"); public static final ConfigBoolean SHIFT_PLACE_ITEMS = new ConfigBoolean("enableShiftPlaceItems", true, "Enables moving all matching stacks at once by holding\nshift while placing items to an empty slot"); - public static final ConfigBoolean VILLAGER_TRADE_LIST = new ConfigBoolean("enableVillagerTradeList", true, "Render a 1.14-style trade list in villager GUIs"); + public static final ConfigBoolean VILLAGER_TRADE_FEATURES = new ConfigBoolean("enableVillagerTradeFeatures", true, "Enable trade favoriting and quick trade features for villagers.\nNote: The Shift + scrolling over the output slot is a separate feature\nand not affected by this option.\nThis option enables middle clicking to mark favorite trades,\nand right clicking on the trade list to fully trade that one trade."); public static final ImmutableList OPTIONS = ImmutableList.of( CRAFTING_FEATURES, @@ -76,12 +95,12 @@ public static class Toggles SCROLL_VILLAGER, SHIFT_DROP_ITEMS, SHIFT_PLACE_ITEMS, - VILLAGER_TRADE_LIST + VILLAGER_TRADE_FEATURES ); } - public static final Set GUI_BLACKLIST = new HashSet(); - public static final Set SLOT_BLACKLIST = new HashSet(); + public static final Set GUI_BLACKLIST = new HashSet<>(); + public static final Set SLOT_BLACKLIST = new HashSet<>(); public static void loadFromFile() { @@ -107,16 +126,16 @@ public static void loadFromFile() CraftingHandler.clearDefinitions(); // "net.minecraft.client.gui.inventory.GuiCrafting,net.minecraft.inventory.SlotCrafting,0,1-9", // vanilla Crafting Table - CraftingHandler.addCraftingGridDefinition(GuiCrafting.class.getName(), SlotCrafting.class.getName(), 0, new SlotRange(1, 9)); - //"net.minecraft.client.gui.inventory.GuiInventory,net.minecraft.inventory.SlotCrafting,0,1-4", // vanilla player inventory crafting grid - CraftingHandler.addCraftingGridDefinition(GuiInventory.class.getName(), SlotCrafting.class.getName(), 0, new SlotRange(1, 4)); + CraftingHandler.addCraftingGridDefinition(CraftingScreen.class.getName(), CraftingResultSlot.class.getName(), 0, new SlotRange(1, 9)); + //"net.minecraft.client.gui.inventory.PlayerInventoryScreen,net.minecraft.inventory.SlotCrafting,0,1-4", // vanilla player inventory crafting grid + CraftingHandler.addCraftingGridDefinition(InventoryScreen.class.getName(), CraftingResultSlot.class.getName(), 0, new SlotRange(1, 4)); } public static void saveToFile() { File dir = FileUtils.getConfigDirectory(); - if (dir.exists() && dir.isDirectory()) + if ((dir.exists() && dir.isDirectory()) || dir.mkdirs()) { JsonObject root = new JsonObject(); diff --git a/src/main/java/fi/dy/masa/itemscroller/config/Hotkeys.java b/src/main/java/fi/dy/masa/itemscroller/config/Hotkeys.java index fb0b408b6..37b7a2680 100644 --- a/src/main/java/fi/dy/masa/itemscroller/config/Hotkeys.java +++ b/src/main/java/fi/dy/masa/itemscroller/config/Hotkeys.java @@ -12,52 +12,62 @@ public class Hotkeys private static final KeybindSettings GUI_RELAXED_CANCEL = KeybindSettings.create(KeybindSettings.Context.GUI, KeyAction.PRESS, true, false, false, true); private static final KeybindSettings GUI_NO_ORDER = KeybindSettings.create(KeybindSettings.Context.GUI, KeyAction.PRESS, false, false, false, true); - public static final ConfigHotkey KEY_OPEN_CONFIG_GUI = new ConfigHotkey("openConfigGui", "I,C", "Open the in-game config GUI"); - - public static final ConfigHotkey KEY_CRAFT_EVERYTHING = new ConfigHotkey("craftEverything", "LCONTROL,C", GUI_NO_ORDER, "Craft everything possible once with the currently selected recipe"); - public static final ConfigHotkey KEY_DROP_ALL_MATCHING = new ConfigHotkey("dropAllMatching", "LCONTROL,LSHIFT,Q", GUI_NO_ORDER, "Drop all stacks identical to the hovered stack"); - public static final ConfigHotkey KEY_MAIN_TOGGLE = new ConfigHotkey("mainToggle", "LCONTROL,S", KeybindSettings.GUI, "Toggle all functionality ON/OFF"); - public static final ConfigHotkey KEY_MASS_CRAFT = new ConfigHotkey("massCraft", "LCONTROL,LMENU,C", GUI_NO_ORDER, "Mass craft and throw out the results with the\ncurrently selected recipe as long as this\nkeybind is held down"); - public static final ConfigHotkey KEY_MOVE_CRAFT_RESULTS = new ConfigHotkey("moveCraftResults", "LCONTROL,M", GUI_NO_ORDER, "Move all of the currently selected recipe's\noutput items from the player inventory\nto the other inventory"); - public static final ConfigHotkey KEY_MOVE_STACK_TO_OFFHAND = new ConfigHotkey("moveStackToOffhand", "F", KeybindSettings.GUI, "Swap the hovered stack with the offhand"); - public static final ConfigHotkey KEY_RECIPE_VIEW = new ConfigHotkey("recipeView", "A", GUI_RELAXED, "Show the Item Scroller recipe GUI"); - public static final ConfigHotkey KEY_SLOT_DEBUG = new ConfigHotkey("slotDebug", "LCONTROL,LMENU,LSHIFT,I", GUI_NO_ORDER, "Print debug info for the hovered slot or GUI"); - public static final ConfigHotkey KEY_STORE_RECIPE = new ConfigHotkey("storeRecipe", "BUTTON2", GUI_RELAXED_CANCEL, "Store a recipe while hovering over a crafting output item"); - public static final ConfigHotkey KEY_THROW_CRAFT_RESULTS = new ConfigHotkey("throwCraftResults", "LCONTROL,T", GUI_NO_ORDER, "Throw all of the currently selected recipe's\noutput items to the ground from the player inventory"); - public static final ConfigHotkey KEY_VILLAGER_TRADE_FAVORITES = new ConfigHotkey("villagerTradeFavorites", "", KeybindSettings.GUI, "Trade everything possible with all the favorited trades\nof the current villager"); - - public static final ConfigHotkey KEY_DRAG_LEAVE_ONE = new ConfigHotkey("keyDragMoveLeaveOne", "LSHIFT,BUTTON1", GUI_NO_ORDER, "Key to move all but the last item from\nall the stacks dragged over"); - public static final ConfigHotkey KEY_DRAG_MATCHING = new ConfigHotkey("keyDragMoveMatching", "LMENU,BUTTON0", GUI_NO_ORDER, "Key to move all matching items dragged over"); - public static final ConfigHotkey KEY_DRAG_MOVE_ONE = new ConfigHotkey("keyDragMoveOne", "LCONTROL,BUTTON0", GUI_NO_ORDER, "Key to move one item from each stack dragged over"); - public static final ConfigHotkey KEY_DRAG_FULL_STACKS = new ConfigHotkey("keyDragMoveStacks", "LSHIFT,BUTTON0", GUI_NO_ORDER, "Key to move the entire stacks dragged over"); - - public static final ConfigHotkey KEY_DRAG_DROP_LEAVE_ONE = new ConfigHotkey("keyDragDropLeaveOne", "LSHIFT,Q,BUTTON1", GUI_NO_ORDER, "Key to drop all but the last item from each stack dragged over"); - public static final ConfigHotkey KEY_DRAG_DROP_SINGLE = new ConfigHotkey("keyDragDropSingle", "Q,BUTTON0", GUI_NO_ORDER, "Key to drop one item from each stack dragged over"); - public static final ConfigHotkey KEY_DRAG_DROP_STACKS = new ConfigHotkey("keyDragDropStacks", "LSHIFT,Q,BUTTON0", GUI_NO_ORDER, "Key to drop the entire stacks dragged over"); - - public static final ConfigHotkey KEY_MOVE_EVERYTHING = new ConfigHotkey("keyMoveEverything", "LMENU,LSHIFT,BUTTON0", GUI_NO_ORDER, "Key to move ALL items to the other\ninventory when clicking a stack"); - - public static final ConfigHotkey KEY_WS_MOVE_DOWN_LEAVE_ONE = new ConfigHotkey("wsMoveDownLeaveOne", "S,BUTTON1", GUI_NO_ORDER, "The key to move all but the last item from each stack\n\"down\" in the inventory"); - public static final ConfigHotkey KEY_WS_MOVE_DOWN_MATCHING = new ConfigHotkey("wsMoveDownMatching", "LMENU,S,BUTTON0", GUI_NO_ORDER, "The key to move all matching items \"down\" in the inventory"); - public static final ConfigHotkey KEY_WS_MOVE_DOWN_SINGLE = new ConfigHotkey("wsMoveDownSingle", "S,BUTTON0", GUI_NO_ORDER, "The key to move single items \"down\" in the inventory"); - public static final ConfigHotkey KEY_WS_MOVE_DOWN_STACKS = new ConfigHotkey("wsMoveDownStacks", "LSHIFT,S,BUTTON0", GUI_NO_ORDER, "The key to move stacks \"down\" in the inventory"); - public static final ConfigHotkey KEY_WS_MOVE_UP_LEAVE_ONE = new ConfigHotkey("wsMoveUpLeaveOne", "W,BUTTON1", GUI_NO_ORDER, "The key to move all but the last item from each stack\n\"up\" in the inventory"); - public static final ConfigHotkey KEY_WS_MOVE_UP_MATCHING = new ConfigHotkey("wsMoveUpMatching", "LMENU,W,BUTTON0", GUI_NO_ORDER, "The key to move all matching items \"up\" in the inventory"); - public static final ConfigHotkey KEY_WS_MOVE_UP_SINGLE = new ConfigHotkey("wsMoveUpSingle", "W,BUTTON0", GUI_NO_ORDER, "The key to move single items \"up\" in the inventory"); - public static final ConfigHotkey KEY_WS_MOVE_UP_STACKS = new ConfigHotkey("wsMoveUpStacks", "LSHIFT,W,BUTTON0", GUI_NO_ORDER, "The key to move stacks \"up\" in the inventory"); - - public static final ConfigHotkey MODIFIER_MOVE_EVERYTHING = new ConfigHotkey("modifierMoveEverything", "LMENU,LSHIFT", GUI_NO_ORDER, "Modifier key to move ALL items to the other\ninventory when scrolling over a stack"); - public static final ConfigHotkey MODIFIER_MOVE_MATCHING = new ConfigHotkey("modifierMoveMatching", "LMENU", GUI_NO_ORDER, "Modifier key to move all matching items to the other\ninventory when scrolling over a stack"); - public static final ConfigHotkey MODIFIER_MOVE_STACK = new ConfigHotkey("modifierMoveStack", "LSHIFT", GUI_NO_ORDER, "Modifier key to move the entire stack to the other\ninventory when scrolling over it"); + public static final ConfigHotkey OPEN_CONFIG_GUI = new ConfigHotkey("openConfigGui", "I,C", "Open the in-game config GUI"); + + public static final ConfigHotkey CRAFT_EVERYTHING = new ConfigHotkey("craftEverything", "LEFT_CONTROL,C", GUI_NO_ORDER, "Craft everything possible once with the currently selected recipe"); + public static final ConfigHotkey DROP_ALL_MATCHING = new ConfigHotkey("dropAllMatching", "LEFT_CONTROL,LEFT_SHIFT,Q", GUI_NO_ORDER, "Drop all stacks identical to the hovered stack"); + public static final ConfigHotkey MASS_CRAFT = new ConfigHotkey("massCraft", "LEFT_CONTROL,LEFT_ALT,C", GUI_NO_ORDER, "Mass craft and throw out the results with the\ncurrently selected recipe as long as this\nkeybind is held down"); + public static final ConfigHotkey MOVE_CRAFT_RESULTS = new ConfigHotkey("moveCraftResults", "LEFT_CONTROL,M", GUI_NO_ORDER, "Move all of the currently selected recipe's\noutput items from the player inventory\nto the other inventory"); + public static final ConfigHotkey RECIPE_VIEW = new ConfigHotkey("recipeView", "A", GUI_RELAXED, "Show the Item Scroller recipe GUI"); + public static final ConfigHotkey SLOT_DEBUG = new ConfigHotkey("slotDebug", "LEFT_CONTROL,LEFT_ALT,LEFT_SHIFT,I", GUI_NO_ORDER, "Print debug info for the hovered slot or GUI"); + public static final ConfigHotkey STORE_RECIPE = new ConfigHotkey("storeRecipe", "BUTTON_3", GUI_RELAXED_CANCEL, "Store a recipe while hovering over a crafting output item"); + public static final ConfigHotkey THROW_CRAFT_RESULTS = new ConfigHotkey("throwCraftResults", "LEFT_CONTROL,T", GUI_NO_ORDER, "Throw all of the currently selected recipe's\noutput items to the ground from the player inventory"); + public static final ConfigHotkey TOGGLE_MOD_ON_OFF = new ConfigHotkey("toggleModOnOff", "", KeybindSettings.GUI, "Toggle all mod functionality ON/OFF"); + public static final ConfigHotkey VILLAGER_TRADE_FAVORITES = new ConfigHotkey("villagerTradeFavorites","", KeybindSettings.GUI, "Trade everything possible with all the favorited trades\nof the current villager"); + + public static final ConfigHotkey KEY_DRAG_DROP_LEAVE_ONE = new ConfigHotkey("keyDragDropLeaveOne", "LEFT_SHIFT,Q,BUTTON_2", GUI_NO_ORDER, "Key to drop all but the last item from each stack dragged over"); + public static final ConfigHotkey KEY_DRAG_DROP_SINGLE = new ConfigHotkey("keyDragDropSingle", "Q,BUTTON_1", GUI_NO_ORDER, "Key to drop one item from each stack dragged over"); + public static final ConfigHotkey KEY_DRAG_DROP_STACKS = new ConfigHotkey("keyDragDropStacks", "LEFT_SHIFT,Q,BUTTON_1", GUI_NO_ORDER, "Key to drop the entire stacks dragged over"); + + public static final ConfigHotkey KEY_DRAG_LEAVE_ONE = new ConfigHotkey("keyDragMoveLeaveOne", "LEFT_SHIFT,BUTTON_2", GUI_NO_ORDER, "Key to move all but the last item from\nall the stacks dragged over"); + public static final ConfigHotkey KEY_DRAG_MATCHING = new ConfigHotkey("keyDragMoveMatching", "LEFT_ALT,BUTTON_1", GUI_NO_ORDER, "Key to move all matching items dragged over"); + public static final ConfigHotkey KEY_DRAG_MOVE_ONE = new ConfigHotkey("keyDragMoveOne", "LEFT_CONTROL,BUTTON_1", GUI_NO_ORDER, "Key to move one item from each stack dragged over"); + public static final ConfigHotkey KEY_DRAG_FULL_STACKS = new ConfigHotkey("keyDragMoveStacks", "LEFT_SHIFT,BUTTON_1", GUI_NO_ORDER, "Key to move the entire stacks dragged over"); + + public static final ConfigHotkey KEY_MOVE_EVERYTHING = new ConfigHotkey("keyMoveEverything", "LEFT_ALT,LEFT_SHIFT,BUTTON_1", GUI_NO_ORDER, "Key to move ALL items to the other\ninventory when clicking a stack"); + + public static final ConfigHotkey KEY_WS_MOVE_DOWN_LEAVE_ONE = new ConfigHotkey("wsMoveDownLeaveOne", "S,BUTTON_2", GUI_NO_ORDER, "The key to move all but the last item from each stack\n\"down\" in the inventory"); + public static final ConfigHotkey KEY_WS_MOVE_DOWN_MATCHING = new ConfigHotkey("wsMoveDownMatching", "LEFT_ALT,S,BUTTON_1", GUI_NO_ORDER, "The key to move all matching items \"down\" in the inventory"); + public static final ConfigHotkey KEY_WS_MOVE_DOWN_SINGLE = new ConfigHotkey("wsMoveDownSingle", "S,BUTTON_1", GUI_NO_ORDER, "The key to move single items \"down\" in the inventory"); + public static final ConfigHotkey KEY_WS_MOVE_DOWN_STACKS = new ConfigHotkey("wsMoveDownStacks", "LEFT_SHIFT,S,BUTTON_1", GUI_NO_ORDER, "The key to move stacks \"down\" in the inventory"); + public static final ConfigHotkey KEY_WS_MOVE_UP_LEAVE_ONE = new ConfigHotkey("wsMoveUpLeaveOne", "W,BUTTON_2", GUI_NO_ORDER, "The key to move all but the last item from each stack\n\"up\" in the inventory"); + public static final ConfigHotkey KEY_WS_MOVE_UP_MATCHING = new ConfigHotkey("wsMoveUpMatching", "LEFT_ALT,W,BUTTON_1", GUI_NO_ORDER, "The key to move all matching items \"up\" in the inventory"); + public static final ConfigHotkey KEY_WS_MOVE_UP_SINGLE = new ConfigHotkey("wsMoveUpSingle", "W,BUTTON_1", GUI_NO_ORDER, "The key to move single items \"up\" in the inventory"); + public static final ConfigHotkey KEY_WS_MOVE_UP_STACKS = new ConfigHotkey("wsMoveUpStacks", "LEFT_SHIFT,W,BUTTON_1", GUI_NO_ORDER, "The key to move stacks \"up\" in the inventory"); + + public static final ConfigHotkey MODIFIER_MOVE_EVERYTHING = new ConfigHotkey("modifierMoveEverything", "LEFT_ALT,LEFT_SHIFT", GUI_NO_ORDER, "Modifier key to move ALL items to the other\ninventory when scrolling over a stack"); + public static final ConfigHotkey MODIFIER_MOVE_MATCHING = new ConfigHotkey("modifierMoveMatching", "LEFT_ALT", GUI_NO_ORDER, "Modifier key to move all matching items to the other\ninventory when scrolling over a stack"); + public static final ConfigHotkey MODIFIER_MOVE_STACK = new ConfigHotkey("modifierMoveStack", "LEFT_SHIFT", GUI_NO_ORDER, "Modifier key to move the entire stack to the other\ninventory when scrolling over it"); + public static final ConfigHotkey MODIFIER_TOGGLE_VILLAGER_GLOBAL_FAVORITE = new ConfigHotkey("modifierToggleVillagerGlobalFavorite", "LEFT_SHIFT", GUI_RELAXED, "Modifier key to hold while middle clicking a trade,\nto toggle the global favorite state for that trade.\nGlobal favorites are used for villagers that don't\nhave any \"local\"/villager-specific favorites set."); public static final List HOTKEY_LIST = ImmutableList.of( - KEY_OPEN_CONFIG_GUI, + OPEN_CONFIG_GUI, + TOGGLE_MOD_ON_OFF, + + CRAFT_EVERYTHING, + DROP_ALL_MATCHING, + MASS_CRAFT, + MOVE_CRAFT_RESULTS, + RECIPE_VIEW, + SLOT_DEBUG, + STORE_RECIPE, + THROW_CRAFT_RESULTS, + VILLAGER_TRADE_FAVORITES, MODIFIER_MOVE_EVERYTHING, MODIFIER_MOVE_MATCHING, MODIFIER_MOVE_STACK, - - KEY_MOVE_EVERYTHING, + MODIFIER_TOGGLE_VILLAGER_GLOBAL_FAVORITE, KEY_DRAG_FULL_STACKS, KEY_DRAG_LEAVE_ONE, @@ -68,17 +78,7 @@ public class Hotkeys KEY_DRAG_DROP_SINGLE, KEY_DRAG_DROP_STACKS, - KEY_CRAFT_EVERYTHING, - KEY_DROP_ALL_MATCHING, - KEY_MAIN_TOGGLE, - KEY_MASS_CRAFT, - KEY_MOVE_CRAFT_RESULTS, - KEY_MOVE_STACK_TO_OFFHAND, - KEY_RECIPE_VIEW, - KEY_SLOT_DEBUG, - KEY_STORE_RECIPE, - KEY_THROW_CRAFT_RESULTS, - KEY_VILLAGER_TRADE_FAVORITES, + KEY_MOVE_EVERYTHING, KEY_WS_MOVE_DOWN_LEAVE_ONE, KEY_WS_MOVE_DOWN_MATCHING, diff --git a/src/main/java/fi/dy/masa/itemscroller/config/ItemScrollerConfigPanel.java b/src/main/java/fi/dy/masa/itemscroller/config/ItemScrollerConfigPanel.java deleted file mode 100644 index 9e2369672..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/config/ItemScrollerConfigPanel.java +++ /dev/null @@ -1,24 +0,0 @@ -package fi.dy.masa.itemscroller.config; - -import fi.dy.masa.itemscroller.Reference; -import fi.dy.masa.malilib.config.gui.ConfigPanelBase; -import fi.dy.masa.malilib.config.gui.GuiModConfigs; - -public class ItemScrollerConfigPanel extends ConfigPanelBase -{ - @Override - protected String getPanelTitlePrefix() - { - return Reference.MOD_NAME + " options"; - } - - @Override - protected void createSubPanels() - { - String modId = Reference.MOD_ID; - - this.addSubPanel((new GuiModConfigs(modId, Configs.Toggles.OPTIONS, "itemscroller.gui.button.config_gui.toggles")).setConfigWidth(100)); - this.addSubPanel((new GuiModConfigs(modId, Configs.Generic.OPTIONS, "itemscroller.gui.button.config_gui.generic")).setConfigWidth(160)); - this.addSubPanel((new GuiModConfigs(modId, Hotkeys.HOTKEY_LIST, "itemscroller.gui.button.config_gui.hotkeys")).setConfigWidth(210)); - } -} diff --git a/src/main/java/fi/dy/masa/itemscroller/event/InputHandler.java b/src/main/java/fi/dy/masa/itemscroller/event/InputHandler.java index 3e9fd188e..66620dd42 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/InputHandler.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/InputHandler.java @@ -1,13 +1,21 @@ package fi.dy.masa.itemscroller.event; -import org.lwjgl.input.Keyboard; +import org.lwjgl.glfw.GLFW; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.entity.passive.MerchantEntity; +import net.minecraft.screen.slot.Slot; +import net.minecraft.util.hit.EntityHitResult; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.MathHelper; import fi.dy.masa.itemscroller.Reference; import fi.dy.masa.itemscroller.config.Configs; import fi.dy.masa.itemscroller.config.Hotkeys; -import fi.dy.masa.itemscroller.gui.widgets.WidgetTradeList; import fi.dy.masa.itemscroller.recipes.RecipeStorage; import fi.dy.masa.itemscroller.util.AccessorUtils; -import fi.dy.masa.itemscroller.util.IGuiMerchant; +import fi.dy.masa.itemscroller.util.ClickPacketBuffer; import fi.dy.masa.itemscroller.util.InputUtils; import fi.dy.masa.itemscroller.util.InventoryUtils; import fi.dy.masa.itemscroller.util.MoveAction; @@ -19,19 +27,7 @@ import fi.dy.masa.malilib.hotkeys.IKeyboardInputHandler; import fi.dy.masa.malilib.hotkeys.IMouseInputHandler; import fi.dy.masa.malilib.util.GuiUtils; -import io.netty.buffer.Unpooled; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiMerchant; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.gui.inventory.GuiContainerCreative; -import net.minecraft.entity.passive.EntityVillager; -import net.minecraft.inventory.ContainerMerchant; -import net.minecraft.inventory.Slot; -import net.minecraft.network.PacketBuffer; -import net.minecraft.network.play.client.CPacketCustomPayload; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.village.MerchantRecipeList; +import fi.dy.masa.malilib.util.KeyCodes; public class InputHandler implements IKeybindProvider, IKeyboardInputHandler, IMouseInputHandler { @@ -58,7 +54,7 @@ public void addHotkeys(IKeybindManager manager) } @Override - public boolean onKeyInput(int eventKey, boolean eventKeyState) + public boolean onKeyInput(int keyCode, int scanCode, int modifiers, boolean eventKeyState) { if (InputUtils.isRecipeViewOpen() && eventKeyState) { @@ -68,23 +64,23 @@ public boolean onKeyInput(int eventKey, boolean eventKeyState) int recipesPerPage = recipes.getRecipeCountPerPage(); int recipeIndexChange = GuiBase.isShiftDown() ? recipesPerPage : recipesPerPage / 2; - if (eventKey >= Keyboard.KEY_1 && eventKey <= Keyboard.KEY_9) + if (keyCode >= KeyCodes.KEY_1 && keyCode <= KeyCodes.KEY_9) { - index = MathHelper.clamp(eventKey - Keyboard.KEY_1, 0, 8); + index = MathHelper.clamp(keyCode - GLFW.GLFW_KEY_1, 0, 8); } - else if (eventKey == Keyboard.KEY_UP && oldIndex > 0) + else if (keyCode == KeyCodes.KEY_UP && oldIndex > 0) { index = oldIndex - 1; } - else if (eventKey == Keyboard.KEY_DOWN && oldIndex < (recipes.getTotalRecipeCount() - 1)) + else if (keyCode == KeyCodes.KEY_DOWN && oldIndex < (recipes.getTotalRecipeCount() - 1)) { index = oldIndex + 1; } - else if (eventKey == Keyboard.KEY_LEFT && oldIndex >= recipeIndexChange) + else if (keyCode == KeyCodes.KEY_LEFT && oldIndex >= recipeIndexChange) { index = oldIndex - recipeIndexChange; } - else if (eventKey == Keyboard.KEY_RIGHT && oldIndex < (recipes.getTotalRecipeCount() - recipeIndexChange)) + else if (keyCode == KeyCodes.KEY_RIGHT && oldIndex < (recipes.getTotalRecipeCount() - recipeIndexChange)) { index = oldIndex + recipeIndexChange; } @@ -96,16 +92,44 @@ else if (eventKey == Keyboard.KEY_RIGHT && oldIndex < (recipes.getTotalRecipeCou } } - return this.handleInput(eventKey, eventKeyState, 0); + return this.handleInput(keyCode, eventKeyState, 0); + } + + @Override + public boolean onMouseScroll(int mouseX, int mouseY, double amount) + { + return this.handleInput(KeyCodes.KEY_NONE, false, amount); } @Override - public boolean onMouseInput(int eventButton, int dWheel, boolean eventButtonState) + public boolean onMouseClick(int mouseX, int mouseY, int eventButton, boolean eventButtonState) { - return this.handleInput(eventButton - 100, eventButtonState, dWheel); + return this.handleInput(eventButton - 100, eventButtonState, 0); } - private boolean handleInput(int keyCode, boolean keyState, int dWheel) + private boolean handleInput(int keyCode, boolean keyState, double dWheel) + { + MinecraftClient mc = MinecraftClient.getInstance(); + + if (mc.player == null) + { + return false; + } + + if (Configs.Generic.RATE_LIMIT_CLICK_PACKETS.getBooleanValue() && + this.callbacks.functionalityEnabled()) + { + ClickPacketBuffer.setShouldBufferClickPackets(true); + } + + boolean cancel = this.handleInputImpl(keyCode, keyState, dWheel, mc); + + ClickPacketBuffer.setShouldBufferClickPackets(false); + + return cancel; + } + + private boolean handleInputImpl(int keyCode, boolean keyState, double dWheel, MinecraftClient mc) { MoveAction action = InventoryUtils.getActiveMoveAction(); @@ -114,7 +138,6 @@ private boolean handleInput(int keyCode, boolean keyState, int dWheel) InventoryUtils.stopDragging(); } - Minecraft mc = Minecraft.getMinecraft(); boolean cancel = false; if (this.callbacks.functionalityEnabled() && mc.player != null) @@ -123,54 +146,27 @@ private boolean handleInput(int keyCode, boolean keyState, int dWheel) final boolean isUse = InputUtils.isUse(keyCode); final boolean isPickBlock = InputUtils.isPickBlock(keyCode); final boolean isAttackUseOrPick = isAttack || isUse || isPickBlock; + final int mouseX = fi.dy.masa.malilib.util.InputUtils.getMouseX(); + final int mouseY = fi.dy.masa.malilib.util.InputUtils.getMouseY(); + Screen screen = GuiUtils.getCurrentScreen(); - if (Configs.Toggles.VILLAGER_TRADE_LIST.getBooleanValue()) + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue()) { VillagerDataStorage storage = VillagerDataStorage.getInstance(); - if (GuiUtils.getCurrentScreen() == null && mc.objectMouseOver != null && - mc.objectMouseOver.typeOfHit == RayTraceResult.Type.ENTITY && - mc.objectMouseOver.entityHit instanceof EntityVillager) + if (screen == null && mc.crosshairTarget != null && + mc.crosshairTarget.getType() == HitResult.Type.ENTITY && + ((EntityHitResult) mc.crosshairTarget).getEntity() instanceof MerchantEntity) { - storage.setLastInteractedUUID(mc.objectMouseOver.entityHit.getUniqueID()); - } - else if (GuiUtils.getCurrentScreen() instanceof GuiMerchant && storage.hasInteractionTarget()) - { - WidgetTradeList widget = ((IGuiMerchant) GuiUtils.getCurrentScreen()).getTradeListWidget(); - - if (widget != null) - { - final int mouseX = InputUtils.getMouseX(); - final int mouseY = InputUtils.getMouseY(); - int mouseButton = isAttack ? 0 : (isUse ? 1 : 2); - - if (widget.isMouseOver(mouseX, mouseY)) - { - if (dWheel != 0) - { - widget.onMouseScrolled(mouseX, mouseY, dWheel); - return true; - } - else if (keyState && isAttackUseOrPick) - { - widget.onMouseClicked(mouseX, mouseY, mouseButton); - return true; - } - } - - if (keyState == false && dWheel == 0) - { - widget.onMouseReleased(mouseX, mouseY, mouseButton); - } - } + storage.setLastInteractedUUID(((EntityHitResult) mc.crosshairTarget).getEntity().getUuid()); } } - if (GuiUtils.getCurrentScreen() instanceof GuiContainer && - (GuiUtils.getCurrentScreen() instanceof GuiContainerCreative) == false && - Configs.GUI_BLACKLIST.contains(GuiUtils.getCurrentScreen().getClass().getName()) == false) + if (screen instanceof HandledScreen && + (screen instanceof CreativeInventoryScreen) == false && + Configs.GUI_BLACKLIST.contains(screen.getClass().getName()) == false) { - GuiContainer gui = (GuiContainer) GuiUtils.getCurrentScreen(); + HandledScreen gui = (HandledScreen) screen; RecipeStorage recipes = RecipeStorage.getInstance(); if (dWheel != 0) @@ -193,8 +189,6 @@ else if (keyState && isAttackUseOrPick) if (keyState && isAttackUseOrPick) { - final int mouseX = InputUtils.getMouseX(); - final int mouseY = InputUtils.getMouseY(); int hoveredRecipeId = RenderEventHandler.instance().getHoveredRecipeId(mouseX, mouseY, recipes, gui); // Hovering over an item in the recipe view @@ -211,11 +205,11 @@ else if (isPickBlock && InputUtils.isRecipeViewOpen() && InventoryUtils.isCrafti } } - InventoryUtils.checkForItemPickup(gui, mc); + InventoryUtils.checkForItemPickup(gui); if (keyState && (isAttack || isUse)) { - InventoryUtils.storeSourceSlotCandidate(slot, mc); + InventoryUtils.storeSourceSlotCandidate(slot, gui); } if (Configs.Toggles.RIGHT_CLICK_CRAFT_STACK.getBooleanValue() && @@ -232,7 +226,7 @@ else if (Configs.Toggles.SHIFT_PLACE_ITEMS.getBooleanValue() && } else if (Configs.Toggles.SHIFT_DROP_ITEMS.getBooleanValue() && isAttack && isShiftDown && - InputUtils.canShiftDropItems(gui, mc)) + InputUtils.canShiftDropItems(gui, mc, mouseX, mouseY)) { cancel |= InventoryUtils.shiftDropItems(gui); } @@ -244,51 +238,40 @@ else if (Configs.Toggles.SHIFT_DROP_ITEMS.getBooleanValue() && } @Override - public void onMouseMoved() + public void onMouseMove(int mouseX, int mouseY) { - Minecraft mc = Minecraft.getMinecraft(); + MinecraftClient mc = MinecraftClient.getInstance(); if (this.callbacks.functionalityEnabled() && mc.player != null && - GuiUtils.getCurrentScreen() instanceof GuiContainer && - Configs.GUI_BLACKLIST.contains(GuiUtils.getCurrentScreen().getClass().getName()) == false) + GuiUtils.getCurrentScreen() instanceof HandledScreen screen && + Configs.GUI_BLACKLIST.contains(screen.getClass().getName()) == false) { - this.handleDragging((GuiContainer) GuiUtils.getCurrentScreen(), mc, false); + this.handleDragging(screen, mc, mouseX, mouseY, false); } } - private boolean handleDragging(GuiContainer gui, Minecraft mc, boolean isClick) + private boolean handleDragging(HandledScreen gui, MinecraftClient mc, int mouseX, int mouseY, boolean isClick) { + boolean cancel = false; MoveAction action = InventoryUtils.getActiveMoveAction(); + if (Configs.Generic.RATE_LIMIT_CLICK_PACKETS.getBooleanValue()) + { + ClickPacketBuffer.setShouldBufferClickPackets(true); + } + if (InputUtils.isActionKeyActive(action)) { - return InventoryUtils.dragMoveItems(gui, mc, action, false); + cancel = InventoryUtils.dragMoveItems(gui, action, mouseX, mouseY, false); } else if (action != MoveAction.NONE) { InventoryUtils.stopDragging(); } - return false; - } - - public static void changeTradePage(GuiMerchant gui, int page) - { - Minecraft mc = Minecraft.getMinecraft(); - MerchantRecipeList trades = gui.getMerchant().getRecipes(mc.player); + ClickPacketBuffer.setShouldBufferClickPackets(false); - // The trade list is unfortunately synced after the GUI - // opens, so the trade list can be null here when we want to - // restore the last viewed page when the GUI first opens - if (page >= 0 && (trades == null || page < trades.size())) - { - ((IGuiMerchant) gui).setSelectedMerchantRecipe(page); - } - - ((ContainerMerchant) gui.inventorySlots).setCurrentRecipeIndex(page); - PacketBuffer packetbuffer = new PacketBuffer(Unpooled.buffer()); - packetbuffer.writeInt(page); - mc.getConnection().sendPacket(new CPacketCustomPayload("MC|TrSel", packetbuffer)); + return cancel; } } diff --git a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java index 2a3839fb3..11074b42c 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java @@ -1,35 +1,36 @@ package fi.dy.masa.itemscroller.event; -import fi.dy.masa.itemscroller.LiteModItemScroller; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.screen.slot.Slot; +import fi.dy.masa.malilib.config.options.ConfigHotkey; +import fi.dy.masa.malilib.gui.GuiBase; +import fi.dy.masa.malilib.gui.Message; +import fi.dy.masa.malilib.hotkeys.IHotkeyCallback; +import fi.dy.masa.malilib.hotkeys.IKeybind; +import fi.dy.masa.malilib.hotkeys.KeyAction; +import fi.dy.masa.malilib.interfaces.IClientTickHandler; +import fi.dy.masa.malilib.util.GuiUtils; +import fi.dy.masa.malilib.util.InfoUtils; +import fi.dy.masa.itemscroller.ItemScroller; import fi.dy.masa.itemscroller.config.Configs; import fi.dy.masa.itemscroller.config.Hotkeys; import fi.dy.masa.itemscroller.gui.GuiConfigs; import fi.dy.masa.itemscroller.recipes.CraftingHandler; -import fi.dy.masa.itemscroller.recipes.CraftingRecipe; +import fi.dy.masa.itemscroller.recipes.RecipePattern; import fi.dy.masa.itemscroller.recipes.RecipeStorage; import fi.dy.masa.itemscroller.util.AccessorUtils; +import fi.dy.masa.itemscroller.util.ClickPacketBuffer; import fi.dy.masa.itemscroller.util.InputUtils; import fi.dy.masa.itemscroller.util.InventoryUtils; import fi.dy.masa.itemscroller.util.MoveAction; -import fi.dy.masa.malilib.config.options.ConfigHotkey; -import fi.dy.masa.malilib.gui.GuiBase; -import fi.dy.masa.malilib.hotkeys.IHotkeyCallback; -import fi.dy.masa.malilib.hotkeys.IKeybind; -import fi.dy.masa.malilib.hotkeys.KeyAction; -import fi.dy.masa.malilib.util.GuiUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.gui.inventory.GuiContainerCreative; -import net.minecraft.client.gui.inventory.GuiInventory; -import net.minecraft.init.SoundEvents; -import net.minecraft.inventory.Slot; - -public class KeybindCallbacks implements IHotkeyCallback + +public class KeybindCallbacks implements IHotkeyCallback, IClientTickHandler { private static final KeybindCallbacks INSTANCE = new KeybindCallbacks(); - private boolean disabled; + protected int massCraftTicker; public static KeybindCallbacks getInstance() { @@ -50,41 +51,54 @@ public void setCallbacks() public boolean functionalityEnabled() { - return this.disabled == false; + return Configs.Generic.MOD_MAIN_TOGGLE.getBooleanValue(); } @Override public boolean onKeyAction(KeyAction action, IKeybind key) { - Minecraft mc = Minecraft.getMinecraft(); - - if (key == Hotkeys.KEY_MAIN_TOGGLE.getKeybind()) + if (Configs.Generic.RATE_LIMIT_CLICK_PACKETS.getBooleanValue()) { - this.disabled = ! this.disabled; + ClickPacketBuffer.setShouldBufferClickPackets(true); + } - if (this.disabled) - { - mc.player.playSound(SoundEvents.BLOCK_NOTE_BASS, 0.8f, 0.8f); - } - else - { - mc.player.playSound(SoundEvents.BLOCK_NOTE_PLING, 0.5f, 1.0f); - } + boolean cancel = this.onKeyActionImpl(action, key); + + ClickPacketBuffer.setShouldBufferClickPackets(false); + + return cancel; + } + private boolean onKeyActionImpl(KeyAction action, IKeybind key) + { + MinecraftClient mc = MinecraftClient.getInstance(); + + if (mc.player == null || mc.world == null) + { + return false; + } + + if (key == Hotkeys.TOGGLE_MOD_ON_OFF.getKeybind()) + { + Configs.Generic.MOD_MAIN_TOGGLE.toggleBooleanValue(); + String msg = this.functionalityEnabled() ? "itemscroller.message.toggled_mod_on" : "itemscroller.message.toggled_mod_off"; + InfoUtils.showGuiOrInGameMessage(Message.MessageType.INFO, msg); return true; } - else if (key == Hotkeys.KEY_OPEN_CONFIG_GUI.getKeybind()) + else if (key == Hotkeys.OPEN_CONFIG_GUI.getKeybind()) { GuiBase.openGui(new GuiConfigs()); return true; } - if (this.disabled || mc == null || mc.player == null || (GuiUtils.getCurrentScreen() instanceof GuiContainer) == false) + if (this.functionalityEnabled() == false || + (GuiUtils.getCurrentScreen() instanceof HandledScreen) == false || + Configs.GUI_BLACKLIST.contains(GuiUtils.getCurrentScreen().getClass().getName())) { return false; } - GuiContainer gui = (GuiContainer) GuiUtils.getCurrentScreen(); + HandledScreen gui = (HandledScreen) GuiUtils.getCurrentScreen(); Slot slot = AccessorUtils.getSlotUnderMouse(gui); RecipeStorage recipes = RecipeStorage.getInstance(); MoveAction moveAction = InputUtils.getDragMoveAction(key); @@ -93,50 +107,43 @@ else if (key == Hotkeys.KEY_OPEN_CONFIG_GUI.getKeybind()) { if (moveAction != MoveAction.NONE) { - return InventoryUtils.dragMoveItems(gui, mc, moveAction, true); + final int mouseX = fi.dy.masa.malilib.util.InputUtils.getMouseX(); + final int mouseY = fi.dy.masa.malilib.util.InputUtils.getMouseY(); + return InventoryUtils.dragMoveItems(gui, moveAction, mouseX, mouseY, true); } else if (key == Hotkeys.KEY_MOVE_EVERYTHING.getKeybind()) { InventoryUtils.tryMoveStacks(slot, gui, false, true, false); return true; } - else if (key == Hotkeys.KEY_DROP_ALL_MATCHING.getKeybind()) + else if (key == Hotkeys.DROP_ALL_MATCHING.getKeybind()) { if (Configs.Toggles.DROP_MATCHING.getBooleanValue() && Configs.GUI_BLACKLIST.contains(gui.getClass().getName()) == false && - slot.getHasStack()) + slot.hasStack()) { InventoryUtils.dropStacks(gui, slot.getStack(), slot, true); return true; } } - else if (key == Hotkeys.KEY_MOVE_STACK_TO_OFFHAND.getKeybind()) - { - // Swap the hovered stack to the Offhand - if ((gui instanceof GuiInventory) && slot != null) - { - InventoryUtils.swapSlots(gui, slot.slotNumber, 45); - return true; - } - } } - if (key == Hotkeys.KEY_CRAFT_EVERYTHING.getKeybind()) + if (key == Hotkeys.CRAFT_EVERYTHING.getKeybind()) { InventoryUtils.craftEverythingPossibleWithCurrentRecipe(recipes.getSelectedRecipe(), gui); return true; } - else if (key == Hotkeys.KEY_THROW_CRAFT_RESULTS.getKeybind()) + else if (key == Hotkeys.THROW_CRAFT_RESULTS.getKeybind()) { InventoryUtils.throwAllCraftingResultsToGround(recipes.getSelectedRecipe(), gui); return true; } - else if (key == Hotkeys.KEY_MOVE_CRAFT_RESULTS.getKeybind()) + else if (key == Hotkeys.MOVE_CRAFT_RESULTS.getKeybind()) { InventoryUtils.moveAllCraftingResultsToOtherInventory(recipes.getSelectedRecipe(), gui); return true; } - else if (key == Hotkeys.KEY_STORE_RECIPE.getKeybind()) + else if (key == Hotkeys.STORE_RECIPE.getKeybind()) { if (InputUtils.isRecipeViewOpen() && InventoryUtils.isCraftingSlot(gui, slot)) { @@ -144,12 +151,11 @@ else if (key == Hotkeys.KEY_STORE_RECIPE.getKeybind()) return true; } } - else if (key == Hotkeys.KEY_VILLAGER_TRADE_FAVORITES.getKeybind()) + else if (key == Hotkeys.VILLAGER_TRADE_FAVORITES.getKeybind()) { - InventoryUtils.villagerTradeEverythingPossibleWithAllFavoritedTrades(); - return true; + return InventoryUtils.villagerTradeEverythingPossibleWithAllFavoritedTrades(); } - else if (key == Hotkeys.KEY_SLOT_DEBUG.getKeybind()) + else if (key == Hotkeys.SLOT_DEBUG.getKeybind()) { if (slot != null) { @@ -157,7 +163,7 @@ else if (key == Hotkeys.KEY_SLOT_DEBUG.getKeybind()) } else { - LiteModItemScroller.logger.info("GUI class: {}", gui.getClass().getName()); + ItemScroller.logger.info("GUI class: {}", gui.getClass().getName()); } return true; @@ -166,46 +172,97 @@ else if (key == Hotkeys.KEY_SLOT_DEBUG.getKeybind()) return false; } - public void onTick(Minecraft mc) + @Override + public void onClientTick(MinecraftClient mc) { - if (this.disabled == false && - mc != null && - mc.player != null && - GuiUtils.getCurrentScreen() instanceof GuiContainer && - (GuiUtils.getCurrentScreen() instanceof GuiContainerCreative) == false && + if (this.functionalityEnabled() == false || mc.player == null) + { + return; + } + + ClickPacketBuffer.sendBufferedPackets(Configs.Generic.PACKET_RATE_LIMIT.getIntegerValue()); + + if (ClickPacketBuffer.shouldCancelWindowClicks()) + { + return; + } + + if (GuiUtils.getCurrentScreen() instanceof HandledScreen gui && + (GuiUtils.getCurrentScreen() instanceof CreativeInventoryScreen) == false && Configs.GUI_BLACKLIST.contains(GuiUtils.getCurrentScreen().getClass().getName()) == false && - Hotkeys.KEY_MASS_CRAFT.getKeybind().isKeybindHeld()) + Hotkeys.MASS_CRAFT.getKeybind().isKeybindHeld()) { - GuiScreen guiScreen = GuiUtils.getCurrentScreen(); - GuiContainer gui = (GuiContainer) guiScreen; + if (++this.massCraftTicker < Configs.Generic.MASS_CRAFT_INTERVAL.getIntegerValue()) + { + return; + } + Slot outputSlot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); if (outputSlot != null) { - CraftingRecipe recipe = RecipeStorage.getInstance().getSelectedRecipe(); - - InventoryUtils.tryClearCursor(gui, mc); - InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); - InventoryUtils.tryMoveItemsToFirstCraftingGrid(recipe, gui, true); + if (Configs.Generic.RATE_LIMIT_CLICK_PACKETS.getBooleanValue()) + { + ClickPacketBuffer.setShouldBufferClickPackets(true); + } - int failsafe = 0; + RecipePattern recipe = RecipeStorage.getInstance().getSelectedRecipe(); + int limit = Configs.Generic.MASS_CRAFT_ITERATIONS.getIntegerValue(); - while (++failsafe < 40 && InventoryUtils.areStacksEqual(outputSlot.getStack(), recipe.getResult())) + if (Configs.Generic.MASS_CRAFT_SWAPS.getBooleanValue()) { - if (Configs.Generic.CARPET_CTRL_Q_CRAFTING.getBooleanValue()) + for (int i = 0; i < limit; ++i) { - InventoryUtils.dropStack(gui, outputSlot.slotNumber); + InventoryUtils.tryClearCursor(gui); + InventoryUtils.setInhibitCraftingOutputUpdate(true); + InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); + InventoryUtils.throwAllNonRecipeItemsToGround(recipe, gui); + InventoryUtils.setCraftingGridContentsUsingSwaps(gui, mc.player.getInventory(), recipe, outputSlot); + InventoryUtils.setInhibitCraftingOutputUpdate(false); + InventoryUtils.updateCraftingOutputSlot(outputSlot); + + if (InventoryUtils.areStacksEqual(outputSlot.getStack(), recipe.getResult()) == false) + { + break; + } + + InventoryUtils.shiftClickSlot(gui, outputSlot.id); } - else + } + else + { + int failsafe = 0; + + while (++failsafe < limit) { - InventoryUtils.dropStacksWhileHasItem(gui, outputSlot.slotNumber, recipe.getResult()); - } + InventoryUtils.tryClearCursor(gui); + InventoryUtils.setInhibitCraftingOutputUpdate(true); + InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); + InventoryUtils.throwAllNonRecipeItemsToGround(recipe, gui); + InventoryUtils.tryMoveItemsToFirstCraftingGrid(recipe, gui, true); + InventoryUtils.setInhibitCraftingOutputUpdate(false); + InventoryUtils.updateCraftingOutputSlot(outputSlot); + + if (InventoryUtils.areStacksEqual(outputSlot.getStack(), recipe.getResult()) == false) + { + break; + } - InventoryUtils.tryClearCursor(gui, mc); - InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); - InventoryUtils.tryMoveItemsToFirstCraftingGrid(recipe, gui, true); + if (Configs.Generic.CARPET_CTRL_Q_CRAFTING.getBooleanValue()) + { + InventoryUtils.dropStack(gui, outputSlot.id); + } + else + { + InventoryUtils.dropStacksWhileHasItem(gui, outputSlot.id, recipe.getResult()); + } + } } + + ClickPacketBuffer.setShouldBufferClickPackets(false); } + + this.massCraftTicker = 0; } } } diff --git a/src/main/java/fi/dy/masa/itemscroller/event/RenderEventHandler.java b/src/main/java/fi/dy/masa/itemscroller/event/RenderEventHandler.java index ac2208621..c80512abe 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/RenderEventHandler.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/RenderEventHandler.java @@ -1,30 +1,30 @@ package fi.dy.masa.itemscroller.event; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.render.DiffuseLighting; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import fi.dy.masa.malilib.render.InventoryOverlay; +import fi.dy.masa.malilib.render.RenderUtils; +import fi.dy.masa.malilib.util.GuiUtils; +import fi.dy.masa.malilib.util.StringUtils; import fi.dy.masa.itemscroller.config.Configs; -import fi.dy.masa.itemscroller.recipes.CraftingRecipe; +import fi.dy.masa.itemscroller.recipes.RecipePattern; import fi.dy.masa.itemscroller.recipes.RecipeStorage; import fi.dy.masa.itemscroller.util.AccessorUtils; +import fi.dy.masa.itemscroller.util.ClickPacketBuffer; import fi.dy.masa.itemscroller.util.InputUtils; import fi.dy.masa.itemscroller.util.InventoryUtils; -import fi.dy.masa.malilib.render.InventoryOverlay; -import fi.dy.masa.malilib.render.RenderUtils; -import fi.dy.masa.malilib.util.GuiUtils; -import fi.dy.masa.malilib.util.StringUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.Vec3d; public class RenderEventHandler { private static final RenderEventHandler INSTANCE = new RenderEventHandler(); - private static final Vec3d LIGHT0_POS = (new Vec3d( 0.2D, 1.0D, -0.7D)).normalize(); - private static final Vec3d LIGHT1_POS = (new Vec3d(-0.2D, 1.0D, 0.7D)).normalize(); + private static final MatrixStack FRESH_MATRIX_STACK = new MatrixStack(); - private final Minecraft mc = Minecraft.getMinecraft(); + private final MinecraftClient mc = MinecraftClient.getInstance(); private int recipeListX; private int recipeListY; private int recipesPerColumn; @@ -40,11 +40,11 @@ public static RenderEventHandler instance() return INSTANCE; } - public void onDrawBackgroundPost() + public void renderRecipeView() { - if (GuiUtils.getCurrentScreen() instanceof GuiContainer && InputUtils.isRecipeViewOpen()) + if (GuiUtils.getCurrentScreen() instanceof HandledScreen && InputUtils.isRecipeViewOpen()) { - GuiContainer gui = (GuiContainer) GuiUtils.getCurrentScreen(); + HandledScreen gui = (HandledScreen) GuiUtils.getCurrentScreen(); RecipeStorage recipes = RecipeStorage.getInstance(); final int first = recipes.getFirstVisibleRecipeId(); final int countPerPage = recipes.getRecipeCountPerPage(); @@ -52,12 +52,14 @@ public void onDrawBackgroundPost() this.calculateRecipePositions(gui); - GlStateManager.pushMatrix(); - GlStateManager.translate(this.recipeListX, this.recipeListY, 0); - GlStateManager.scale(this.scale, this.scale, 1); + MatrixStack matrixStack = RenderSystem.getModelViewStack(); + matrixStack.push(); + matrixStack.translate(this.recipeListX, this.recipeListY, 0); + matrixStack.scale((float) this.scale, (float) this.scale, 1); + RenderSystem.applyModelViewMatrix(); String str = StringUtils.translate("itemscroller.gui.label.recipe_page", (first / countPerPage) + 1, recipes.getTotalRecipeCount() / countPerPage); - this.mc.fontRenderer.drawString(str, 16, -12, 0xC0C0C0C0); + this.mc.textRenderer.draw(matrixStack, str, 16, -12, 0xC0C0C0C0); for (int i = 0, recipeId = first; recipeId <= lastOnPage; ++i, ++recipeId) { @@ -66,54 +68,78 @@ public void onDrawBackgroundPost() int row = i % this.recipesPerColumn; int column = i / this.recipesPerColumn; - this.renderStoredRecipeStack(stack, recipeId, row, column, gui, selected); + this.renderStoredRecipeStack(stack, recipeId, row, column, gui, selected, matrixStack); } if (Configs.Generic.CRAFTING_RENDER_RECIPE_ITEMS.getBooleanValue()) { - final int mouseX = InputUtils.getMouseX(); - final int mouseY = InputUtils.getMouseY(); + final int mouseX = fi.dy.masa.malilib.util.InputUtils.getMouseX(); + final int mouseY = fi.dy.masa.malilib.util.InputUtils.getMouseY(); final int recipeId = this.getHoveredRecipeId(mouseX, mouseY, recipes, gui); - CraftingRecipe recipe = recipeId >= 0 ? recipes.getRecipe(recipeId) : recipes.getSelectedRecipe(); + RecipePattern recipe = recipeId >= 0 ? recipes.getRecipe(recipeId) : recipes.getSelectedRecipe(); this.renderRecipeItems(recipe, recipes.getRecipeCountPerPage(), gui); } - GlStateManager.popMatrix(); - GlStateManager.enableBlend(); // Fixes the crafting book icon rendering + matrixStack.pop(); + RenderSystem.applyModelViewMatrix(); + RenderSystem.enableBlend(); // Fixes the crafting book icon rendering } } - public void onDrawScreenPost() + public void onDrawScreenPost(MinecraftClient mc) { - if (GuiUtils.getCurrentScreen() instanceof GuiContainer && InputUtils.isRecipeViewOpen()) + this.renderRecipeView(); + + if (GuiUtils.getCurrentScreen() instanceof HandledScreen) { - GuiContainer gui = (GuiContainer) GuiUtils.getCurrentScreen(); + HandledScreen gui = (HandledScreen) this.mc.currentScreen; + + int bufferedCount = ClickPacketBuffer.getBufferedActionsCount(); + + if (bufferedCount > 0) + { + mc.textRenderer.draw(FRESH_MATRIX_STACK, "Buffered slot clicks: " + bufferedCount, 10, 10, 0xFFD0D0D0); + } + + if (InputUtils.isRecipeViewOpen() == false) + { + return; + } + RecipeStorage recipes = RecipeStorage.getInstance(); - final int mouseX = InputUtils.getMouseX(); - final int mouseY = InputUtils.getMouseY(); + final int mouseX = fi.dy.masa.malilib.util.InputUtils.getMouseX(); + final int mouseY = fi.dy.masa.malilib.util.InputUtils.getMouseY(); final int recipeId = this.getHoveredRecipeId(mouseX, mouseY, recipes, gui); + float offset = 300f; + MatrixStack matrixStack = RenderSystem.getModelViewStack(); + matrixStack.push(); + matrixStack.translate(0, 0, offset); + if (recipeId >= 0) { - CraftingRecipe recipe = recipes.getRecipe(recipeId); - this.renderHoverTooltip(mouseX, mouseY, recipe, gui); + RecipePattern recipe = recipes.getRecipe(recipeId); + this.renderHoverTooltip(mouseX, mouseY, recipe, gui, FRESH_MATRIX_STACK); } else if (Configs.Generic.CRAFTING_RENDER_RECIPE_ITEMS.getBooleanValue()) { - CraftingRecipe recipe = recipes.getSelectedRecipe(); + RecipePattern recipe = recipes.getSelectedRecipe(); ItemStack stack = this.getHoveredRecipeIngredient(mouseX, mouseY, recipe, recipes.getRecipeCountPerPage(), gui); if (InventoryUtils.isStackEmpty(stack) == false) { - InventoryOverlay.renderStackToolTip(mouseX, mouseY, stack, this.mc); + InventoryOverlay.renderStackToolTip(mouseX, mouseY, stack, this.mc, FRESH_MATRIX_STACK); } } + + matrixStack.pop(); + RenderSystem.applyModelViewMatrix(); } } - private void calculateRecipePositions(GuiContainer gui) + private void calculateRecipePositions(HandledScreen gui) { RecipeStorage recipes = RecipeStorage.getInstance(); final int gapHorizontal = 2; @@ -137,24 +163,24 @@ private void calculateRecipePositions(GuiContainer gui) int maxStackDimensionsHorizontal = (int) (((usableWidth - (this.columns * (this.numberTextWidth + this.gapColumn))) / (this.columns + 3 + 0.8)) * gapScaleHorizontal); int stackDimensions = (int) Math.min(maxStackDimensionsVertical, maxStackDimensionsHorizontal); - this.scale = (int) Math.ceil(((double) stackDimensions / (double) stackBaseHeight)); + this.scale = (double) stackDimensions / (double) stackBaseHeight; this.entryHeight = stackBaseHeight + gapVertical; this.recipeListX = guiLeft - (int) ((this.columns * (stackBaseHeight + this.numberTextWidth + this.gapColumn) + gapHorizontal) * this.scale); this.recipeListY = (int) (this.entryHeight * this.scale); this.columnWidth = stackBaseHeight + this.numberTextWidth + this.gapColumn; } - private void renderHoverTooltip(int mouseX, int mouseY, CraftingRecipe recipe, GuiContainer gui) + private void renderHoverTooltip(int mouseX, int mouseY, RecipePattern recipe, HandledScreen gui, MatrixStack matrixStack) { ItemStack stack = recipe.getResult(); if (InventoryUtils.isStackEmpty(stack) == false) { - InventoryOverlay.renderStackToolTip(mouseX, mouseY, stack, this.mc); + InventoryOverlay.renderStackToolTip(mouseX, mouseY, stack, this.mc, matrixStack); } } - public int getHoveredRecipeId(int mouseX, int mouseY, RecipeStorage recipes, GuiContainer gui) + public int getHoveredRecipeId(int mouseX, int mouseY, RecipeStorage recipes, HandledScreen gui) { if (InputUtils.isRecipeViewOpen()) { @@ -183,29 +209,31 @@ public int getHoveredRecipeId(int mouseX, int mouseY, RecipeStorage recipes, Gui return -1; } - private void renderStoredRecipeStack(ItemStack stack, int recipeId, int row, int column, GuiContainer gui, boolean selected) + private void renderStoredRecipeStack(ItemStack stack, int recipeId, int row, int column, HandledScreen gui, + boolean selected, MatrixStack matrixStack) { - final FontRenderer font = this.mc.fontRenderer; + final TextRenderer font = this.mc.textRenderer; final String indexStr = String.valueOf(recipeId + 1); int x = column * this.columnWidth + this.gapColumn + this.numberTextWidth; int y = row * this.entryHeight; this.renderStackAt(stack, x, y, selected); - double scale = 0.75; - x = x - (int) (font.getStringWidth(indexStr) * scale) - 2; - y = row * this.entryHeight + this.entryHeight / 2 - font.FONT_HEIGHT / 2; + float scale = 0.75F; + x = x - (int) (font.getWidth(indexStr) * scale) - 2; + y = row * this.entryHeight + this.entryHeight / 2 - font.fontHeight / 2; - GlStateManager.pushMatrix(); - GlStateManager.translate(x, y, 0); - GlStateManager.scale(scale, scale, 0); + matrixStack = FRESH_MATRIX_STACK; + matrixStack.push(); + matrixStack.translate(x, y, 0); + matrixStack.scale(scale, scale, 1); - font.drawString(indexStr, 0, 0, 0xC0C0C0); + font.draw(matrixStack, indexStr, 0, 0, 0xFFC0C0C0); - GlStateManager.popMatrix(); + matrixStack.pop(); } - private void renderRecipeItems(CraftingRecipe recipe, int recipeCountPerPage, GuiContainer gui) + private void renderRecipeItems(RecipePattern recipe, int recipeCountPerPage, HandledScreen gui) { ItemStack[] items = recipe.getRecipeItems(); final int recipeDimensions = (int) Math.ceil(Math.sqrt(recipe.getRecipeLength())); @@ -224,7 +252,7 @@ private void renderRecipeItems(CraftingRecipe recipe, int recipeCountPerPage, Gu } } - private ItemStack getHoveredRecipeIngredient(int mouseX, int mouseY, CraftingRecipe recipe, int recipeCountPerPage, GuiContainer gui) + private ItemStack getHoveredRecipeIngredient(int mouseX, int mouseY, RecipePattern recipe, int recipeCountPerPage, HandledScreen gui) { final int recipeDimensions = (int) Math.ceil(Math.sqrt(recipe.getRecipeLength())); int scaledStackDimensions = (int) (16 * this.scale); @@ -258,73 +286,74 @@ private ItemStack getHoveredRecipeIngredient(int mouseX, int mouseY, CraftingRec private void renderStackAt(ItemStack stack, int x, int y, boolean border) { - GlStateManager.pushMatrix(); - GlStateManager.disableLighting(); final int w = 16; if (border) { // Draw a light/white border around the stack - RenderUtils.drawRect(x - 1, y - 1, w + 1, 1 , 0xFFFFFFFF); - RenderUtils.drawRect(x - 1, y , 1 , w + 1, 0xFFFFFFFF); - RenderUtils.drawRect(x + w, y - 1, 1 , w + 1, 0xFFFFFFFF); - RenderUtils.drawRect(x , y + w, w + 1, 1 , 0xFFFFFFFF); - - RenderUtils.drawRect(x, y, w, w, 0x20FFFFFF); // light background for the item - - } - else - { - RenderUtils.drawRect(x, y, w, w, 0x20FFFFFF); // light background for the item + RenderUtils.drawOutline(x - 1, y - 1, w + 2, w + 2, 0xFFFFFFFF); } + RenderUtils.drawRect(x, y, w, w, 0x20FFFFFF); // light background for the item + if (InventoryUtils.isStackEmpty(stack) == false) { - enableGUIStandardItemLighting((float) this.scale); + DiffuseLighting.enableGuiDepthLighting(); stack = stack.copy(); InventoryUtils.setStackSize(stack, 1); - this.mc.getRenderItem().zLevel += 100; - this.mc.getRenderItem().renderItemAndEffectIntoGUI(mc.player, stack, x, y); - this.mc.getRenderItem().zLevel -= 100; - } - RenderUtils.disableItemLighting(); - GlStateManager.disableBlend(); - GlStateManager.popMatrix(); + MatrixStack matrixStack = new MatrixStack(); + matrixStack.translate(0, 0, 100.f); + this.mc.getItemRenderer().renderInGui(matrixStack, stack, x, y); + } } + /* public static void enableGUIStandardItemLighting(float scale) { - GlStateManager.pushMatrix(); - GlStateManager.rotate(-30.0F, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(165.0F, 1.0F, 0.0F, 0.0F); + RenderSystem.pushMatrix(); + RenderSystem.rotatef(-30.0F, 0.0F, 1.0F, 0.0F); + RenderSystem.rotatef(165.0F, 1.0F, 0.0F, 0.0F); enableStandardItemLighting(scale); - GlStateManager.popMatrix(); + RenderSystem.popMatrix(); } public static void enableStandardItemLighting(float scale) { - GlStateManager.enableLighting(); + RenderSystem.enableLighting(); GlStateManager.enableLight(0); GlStateManager.enableLight(1); - GlStateManager.enableColorMaterial(); - GlStateManager.colorMaterial(1032, 5634); - GlStateManager.glLight(16384, 4611, RenderHelper.setColorBuffer((float) LIGHT0_POS.x, (float) LIGHT0_POS.y, (float) LIGHT0_POS.z, 0.0f)); + RenderSystem.enableColorMaterial(); + RenderSystem.colorMaterial(1032, 5634); float lightStrength = 0.3F * scale; - GlStateManager.glLight(16384, 4609, RenderHelper.setColorBuffer(lightStrength, lightStrength, lightStrength, 1.0F)); - GlStateManager.glLight(16384, 4608, RenderHelper.setColorBuffer(0.0F, 0.0F, 0.0F, 1.0F)); - GlStateManager.glLight(16384, 4610, RenderHelper.setColorBuffer(0.0F, 0.0F, 0.0F, 1.0F)); - GlStateManager.glLight(16385, 4611, RenderHelper.setColorBuffer((float) LIGHT1_POS.x, (float) LIGHT1_POS.y, (float) LIGHT1_POS.z, 0.0f)); - GlStateManager.glLight(16385, 4609, RenderHelper.setColorBuffer(lightStrength, lightStrength, lightStrength, 1.0F)); - GlStateManager.glLight(16385, 4608, RenderHelper.setColorBuffer(0.0F, 0.0F, 0.0F, 1.0F)); - GlStateManager.glLight(16385, 4610, RenderHelper.setColorBuffer(0.0F, 0.0F, 0.0F, 1.0F)); - GlStateManager.shadeModel(7424); - float ambientLightStrength = 0.4F; - GlStateManager.glLightModel(2899, RenderHelper.setColorBuffer(ambientLightStrength, ambientLightStrength, ambientLightStrength, 1.0F)); + + GlStateManager.light(16384, 4611, singletonBuffer((float) LIGHT0_POS.x, (float) LIGHT0_POS.y, (float) LIGHT0_POS.z, 0.0f)); + GlStateManager.light(16384, 4609, singletonBuffer(lightStrength, lightStrength, lightStrength, 1.0F)); + GlStateManager.light(16384, 4608, singletonBuffer(0.0F, 0.0F, 0.0F, 1.0F)); + GlStateManager.light(16384, 4610, singletonBuffer(0.0F, 0.0F, 0.0F, 1.0F)); + + GlStateManager.light(16385, 4611, singletonBuffer((float) LIGHT1_POS.x, (float) LIGHT1_POS.y, (float) LIGHT1_POS.z, 0.0f)); + GlStateManager.light(16385, 4609, singletonBuffer(lightStrength, lightStrength, lightStrength, 1.0F)); + GlStateManager.light(16385, 4608, singletonBuffer(0.0F, 0.0F, 0.0F, 1.0F)); + GlStateManager.light(16385, 4610, singletonBuffer(0.0F, 0.0F, 0.0F, 1.0F)); + + RenderSystem.shadeModel(GL11.GL_FLAT); + + GlStateManager.lightModel(2899, singletonBuffer(ambientLightStrength, ambientLightStrength, ambientLightStrength, 1.0F)); + } + + private static FloatBuffer singletonBuffer(float val1, float val2, float val3, float val4) + { + FLOAT_BUFFER.clear(); + FLOAT_BUFFER.put(val1).put(val2).put(val3).put(val4); + FLOAT_BUFFER.flip(); + + return FLOAT_BUFFER; } + */ } diff --git a/src/main/java/fi/dy/masa/itemscroller/event/WorldLoadListener.java b/src/main/java/fi/dy/masa/itemscroller/event/WorldLoadListener.java index 44daf4132..67adf9cae 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/WorldLoadListener.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/WorldLoadListener.java @@ -1,32 +1,41 @@ package fi.dy.masa.itemscroller.event; import javax.annotation.Nullable; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.world.ClientWorld; import fi.dy.masa.itemscroller.config.Configs; import fi.dy.masa.itemscroller.recipes.RecipeStorage; +import fi.dy.masa.itemscroller.util.ClickPacketBuffer; import fi.dy.masa.itemscroller.villager.VillagerDataStorage; import fi.dy.masa.malilib.interfaces.IWorldLoadListener; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.WorldClient; public class WorldLoadListener implements IWorldLoadListener { @Override - public void onWorldLoadPre(@Nullable WorldClient worldBefore, @Nullable WorldClient worldAfter, Minecraft mc) + public void onWorldLoadPre(@Nullable ClientWorld worldBefore, @Nullable ClientWorld worldAfter, MinecraftClient mc) { // Quitting to main menu, save the settings before the integrated server gets shut down if (worldBefore != null && worldAfter == null) { this.writeData(); + VillagerDataStorage.getInstance().writeToDisk(); } } @Override - public void onWorldLoadPost(@Nullable WorldClient worldBefore, @Nullable WorldClient worldAfter, Minecraft mc) + public void onWorldLoadPost(@Nullable ClientWorld worldBefore, @Nullable ClientWorld worldAfter, MinecraftClient mc) { // Logging in to a world, load the data if (worldBefore == null && worldAfter != null) { this.readStoredData(); + VillagerDataStorage.getInstance().readFromDisk(); + } + + // Logging out + if (worldAfter == null) + { + ClickPacketBuffer.reset(); } } @@ -36,8 +45,6 @@ private void writeData() { RecipeStorage.getInstance().writeToDisk(); } - - VillagerDataStorage.getInstance().writeToDisk(); } private void readStoredData() @@ -46,7 +53,5 @@ private void readStoredData() { RecipeStorage.getInstance().readFromDisk(); } - - VillagerDataStorage.getInstance().readFromDisk(); - } + } } diff --git a/src/main/java/fi/dy/masa/itemscroller/gui/GuiConfigs.java b/src/main/java/fi/dy/masa/itemscroller/gui/GuiConfigs.java index e4a9efef3..8cdafadb0 100644 --- a/src/main/java/fi/dy/masa/itemscroller/gui/GuiConfigs.java +++ b/src/main/java/fi/dy/masa/itemscroller/gui/GuiConfigs.java @@ -2,6 +2,7 @@ import java.util.Collections; import java.util.List; +import com.google.common.collect.ImmutableList; import fi.dy.masa.itemscroller.Reference; import fi.dy.masa.itemscroller.config.Configs; import fi.dy.masa.itemscroller.config.Hotkeys; @@ -14,7 +15,7 @@ public class GuiConfigs extends GuiConfigsBase { - private static ConfigGuiTab tab = ConfigGuiTab.HOTKEYS; + private static ConfigGuiTab tab = ConfigGuiTab.GENERIC; public GuiConfigs() { @@ -30,7 +31,7 @@ public void initGui() int x = 10; int y = 26; - for (ConfigGuiTab tab : ConfigGuiTab.values()) + for (ConfigGuiTab tab : ConfigGuiTab.VALUES) { x += this.createButton(x, y, -1, tab); } @@ -114,7 +115,9 @@ public enum ConfigGuiTab private final String translationKey; - private ConfigGuiTab(String translationKey) + public static final ImmutableList VALUES = ImmutableList.copyOf(values()); + + ConfigGuiTab(String translationKey) { this.translationKey = translationKey; } diff --git a/src/main/java/fi/dy/masa/itemscroller/gui/widgets/Icons.java b/src/main/java/fi/dy/masa/itemscroller/gui/ItemScrollerIcons.java similarity index 75% rename from src/main/java/fi/dy/masa/itemscroller/gui/widgets/Icons.java rename to src/main/java/fi/dy/masa/itemscroller/gui/ItemScrollerIcons.java index e979b1df3..2abf639d6 100644 --- a/src/main/java/fi/dy/masa/itemscroller/gui/widgets/Icons.java +++ b/src/main/java/fi/dy/masa/itemscroller/gui/ItemScrollerIcons.java @@ -1,18 +1,19 @@ -package fi.dy.masa.itemscroller.gui.widgets; +package fi.dy.masa.itemscroller.gui; +import net.minecraft.util.Identifier; import fi.dy.masa.itemscroller.Reference; import fi.dy.masa.malilib.gui.interfaces.IGuiIcon; import fi.dy.masa.malilib.render.RenderUtils; -import net.minecraft.util.ResourceLocation; -public enum Icons implements IGuiIcon +public enum ItemScrollerIcons implements IGuiIcon { TRADE_ARROW_AVAILABLE (112, 0, 10, 9), TRADE_ARROW_LOCKED (112, 9, 10, 9), SCROLL_BAR_6 (106, 0, 6, 167), - STAR_5 (112, 18, 5, 5); + STAR_5_YELLOW (112, 18, 5, 5), + STAR_5_PURPLE (117, 18, 5, 5); - public static final ResourceLocation TEXTURE = new ResourceLocation(Reference.MOD_ID, "textures/gui/gui_widgets.png"); + public static final Identifier TEXTURE = new Identifier(Reference.MOD_ID, "textures/gui/gui_widgets.png"); private final int u; private final int v; @@ -21,12 +22,12 @@ public enum Icons implements IGuiIcon private final int hoverOffU; private final int hoverOffV; - private Icons(int u, int v, int w, int h) + ItemScrollerIcons(int u, int v, int w, int h) { this(u, v, w, h, w, 0); } - private Icons(int u, int v, int w, int h, int hoverOffU, int hoverOffV) + ItemScrollerIcons(int u, int v, int w, int h, int hoverOffU, int hoverOffV) { this.u = u; this.v = v; @@ -82,7 +83,7 @@ public void renderAt(int x, int y, float zLevel, boolean enabled, boolean select } @Override - public ResourceLocation getTexture() + public Identifier getTexture() { return TEXTURE; } diff --git a/src/main/java/fi/dy/masa/itemscroller/gui/widgets/WidgetTradeEntry.java b/src/main/java/fi/dy/masa/itemscroller/gui/widgets/WidgetTradeEntry.java deleted file mode 100644 index 1d583a73f..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/gui/widgets/WidgetTradeEntry.java +++ /dev/null @@ -1,133 +0,0 @@ -package fi.dy.masa.itemscroller.gui.widgets; - -import com.google.common.collect.ImmutableList; -import fi.dy.masa.itemscroller.villager.VillagerData; -import fi.dy.masa.malilib.gui.GuiBase; -import fi.dy.masa.malilib.gui.interfaces.IGuiIcon; -import fi.dy.masa.malilib.gui.widgets.WidgetListEntryBase; -import fi.dy.masa.malilib.render.InventoryOverlay; -import fi.dy.masa.malilib.render.RenderUtils; -import fi.dy.masa.malilib.util.StringUtils; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraft.village.MerchantRecipe; - -public class WidgetTradeEntry extends WidgetListEntryBase -{ - public static final ResourceLocation BUTTON_TEXTURE = new ResourceLocation("textures/gui/widgets.png"); - - private final VillagerData data; - - public WidgetTradeEntry(int x, int y, int width, int height, - MerchantRecipe entry, int listIndex, VillagerData data) - { - super(x, y, width, height, entry, listIndex); - - this.data = data; - } - - @Override - public void render(int mouseX, int mouseY, boolean selected) - { - RenderUtils.color(1f, 1f, 1f, 1f); - - this.bindTexture(BUTTON_TEXTURE); - - int v = 66; - - if (this.isMouseOver(mouseX, mouseY)) - { - v += 20; - } - - // Button background texture for the trades - RenderUtils.drawTexturedRect(this.x , this.y, 0, v, this.width - 4, this.height); - RenderUtils.drawTexturedRect(this.x + this.width - 4, this.y, 196, v, 4, this.height); - - if (selected) - { - RenderUtils.drawOutline(this.x, this.y, this.width, this.height, 0xFFFFB000, 1f); - } - - this.bindTexture(Icons.TEXTURE); - - IGuiIcon icon = this.entry.isRecipeDisabled() ? Icons.TRADE_ARROW_LOCKED : Icons.TRADE_ARROW_AVAILABLE; - - RenderUtils.color(1f, 1f, 1f, 1f); - RenderUtils.setupBlend(); - GlStateManager.enableAlpha(); - - // Trade arrow - icon.renderAt(this.x + 44, this.y + 5, 1f, false, false); - - // This entry has been favorited - if (this.data.getFavorites().contains(this.getListIndex())) - { - Icons.STAR_5.renderAt(this.x + 80, this.y + 2, 1f, false, false); - } - - GlStateManager.disableBlend(); - - ItemStack buy1 = this.entry.getItemToBuy(); - ItemStack buy2 = this.entry.getSecondItemToBuy(); - ItemStack sell = this.entry.getItemToSell(); - - if (buy1.isEmpty() == false) - { - InventoryOverlay.renderStackAt(buy1, this.x + 4, this.y + 2, 1, this.mc); - } - - if (buy2.isEmpty() == false) - { - InventoryOverlay.renderStackAt(buy2, this.x + 22, this.y + 2, 1, this.mc); - } - - if (sell.isEmpty() == false) - { - InventoryOverlay.renderStackAt(sell, this.x + 60, this.y + 2, 1, this.mc); - } - } - - @Override - public void postRenderHovered(int mouseX, int mouseY, boolean selected) - { - if (mouseY >= this.y + 2 && mouseY <= this.y + this.height - 2) - { - if (mouseX >= this.x + 4 && mouseX <= this.x + 4 + 16) - { - ItemStack buy1 = this.entry.getItemToBuy(); - - if (buy1.isEmpty() == false) - { - InventoryOverlay.renderStackToolTip(mouseX, mouseY, buy1, this.mc); - } - } - else if (mouseX >= this.x + 22 && mouseX <= this.x + 22 + 16) - { - ItemStack buy2 = this.entry.getSecondItemToBuy(); - - if (buy2.isEmpty() == false) - { - InventoryOverlay.renderStackToolTip(mouseX, mouseY, buy2, this.mc); - } - } - else if (mouseX >= this.x + 60 && mouseX <= this.x + 60 + 16) - { - ItemStack sell = this.entry.getItemToSell(); - - if (sell.isEmpty() == false) - { - InventoryOverlay.renderStackToolTip(mouseX, mouseY, sell, this.mc); - } - } - - if (GuiBase.isAltDown()) - { - int uses = this.entry.getToolUses(); - int max = this.entry.getMaxTradeUses(); - RenderUtils.drawHoverText(mouseX + 6, mouseY + 18, ImmutableList.of(StringUtils.translate("itemscroller.gui.label.trade_uses", uses, max))); - } - } - } -} diff --git a/src/main/java/fi/dy/masa/itemscroller/gui/widgets/WidgetTradeList.java b/src/main/java/fi/dy/masa/itemscroller/gui/widgets/WidgetTradeList.java deleted file mode 100644 index bda7dd514..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/gui/widgets/WidgetTradeList.java +++ /dev/null @@ -1,231 +0,0 @@ -package fi.dy.masa.itemscroller.gui.widgets; - -import java.util.ArrayList; -import java.util.List; -import fi.dy.masa.itemscroller.event.InputHandler; -import fi.dy.masa.itemscroller.util.AccessorUtils; -import fi.dy.masa.itemscroller.util.InventoryUtils; -import fi.dy.masa.itemscroller.villager.VillagerData; -import fi.dy.masa.itemscroller.villager.VillagerDataStorage; -import fi.dy.masa.malilib.gui.GuiBase; -import fi.dy.masa.malilib.gui.GuiScrollBar; -import fi.dy.masa.malilib.gui.widgets.WidgetBase; -import fi.dy.masa.malilib.render.RenderUtils; -import fi.dy.masa.malilib.util.StringUtils; -import net.minecraft.client.gui.GuiMerchant; -import net.minecraft.village.MerchantRecipe; -import net.minecraft.village.MerchantRecipeList; - -public class WidgetTradeList extends WidgetBase -{ - private final GuiScrollBar scrollBar; - private final GuiMerchant parentGui; - private final VillagerDataStorage storage; - private final ArrayList entryList = new ArrayList<>(); - private final VillagerData data; - private MerchantRecipeList recipeList; - private int scrollBarTotalHeight; - - public WidgetTradeList(int x, int y, GuiMerchant parentGui, VillagerData data) - { - super(x, y, 106, 166); - - this.scrollBar = (new GuiScrollBar(Icons.SCROLL_BAR_6)).setRenderBarBackground(false); - this.parentGui = parentGui; - this.storage = VillagerDataStorage.getInstance(); - this.data = data; - } - - private void lazySetRecipeList() - { - if (this.recipeList == null) - { - this.recipeList = this.parentGui.getMerchant().getRecipes(this.mc.player); - - if (this.recipeList != null) - { - int max = Math.max(0, this.recipeList.size() - 7); - this.scrollBar.setMaxValue(max); - this.scrollBar.setValue(this.data.getTradeListPosition()); - this.scrollBarTotalHeight = Math.max(140, this.recipeList.size() * 20); - - this.reCreateEntryWidgets(); - } - } - } - - @Override - public boolean isMouseOver(int mouseX, int mouseY) - { - return mouseX >= this.x + 5 && mouseX <= this.x + 99 && - mouseY >= this.y + 18 && mouseY <= this.y + 157; - } - - @Override - protected boolean onMouseClickedImpl(int mouseX, int mouseY, int mouseButton) - { - if (this.scrollBar.wasMouseOver()) - { - this.scrollBar.setIsDragging(true); - return true; - } - - if (mouseX <= this.x + 92) - { - int relY = mouseY - (this.y + 18); - int listIndex = relY / 20; - WidgetTradeEntry entry = listIndex >= 0 && listIndex < this.entryList.size() ? this.entryList.get(listIndex) : null; - int recipeIndex = entry != null ? entry.getListIndex() : -1; - - if (recipeIndex >= 0) - { - // Middle click to toggle favorites - if (mouseButton == 2) - { - this.storage.toggleFavorite(recipeIndex); - this.reCreateEntryWidgets(); - } - else - { - boolean samePage = AccessorUtils.getSelectedMerchantRecipe(this.parentGui) == recipeIndex; - InputHandler.changeTradePage(this.parentGui, recipeIndex); - - if (GuiBase.isShiftDown() || samePage || mouseButton == 1) - { - InventoryUtils.villagerClearTradeInputSlots(); - - if (mouseButton == 1) - { - InventoryUtils.villagerTradeEverythingPossibleWithCurrentRecipe(); - } - else - { - InventoryUtils.tryMoveItemsToMerchantBuySlots(this.parentGui, true); - } - } - } - } - } - - return true; - } - - @Override - public void onMouseReleasedImpl(int mouseX, int mouseY, int mouseButton) - { - this.scrollBar.setIsDragging(false); - } - - @Override - public boolean onMouseScrolledImpl(int mouseX, int mouseY, double mouseWheelDelta) - { - this.scrollBar.offsetValue(mouseWheelDelta < 0 ? 1 : -1); - return true; - } - - @Override - public void render(int mouseX, int mouseY, boolean selected) - { - this.lazySetRecipeList(); - - if (this.recipeList != null) - { - int currentPage = AccessorUtils.getSelectedMerchantRecipe(this.parentGui); - currentPage = Math.min(currentPage, this.recipeList.size() - 1); - this.updateDataStorage(currentPage); - - RenderUtils.color(1f, 1f, 1f, 1f); - RenderUtils.disableItemLighting(); - this.bindTexture(Icons.TEXTURE); - - // Background - RenderUtils.drawTexturedRect(this.x, this.y, 0, 0, this.width, 166); - - String str = StringUtils.translate("itemscroller.gui.label.trades"); - int w = this.getStringWidth(str); - this.drawString(this.x + this.width / 2 - w / 2, this.y + 6, 0xFF404040, str); - - this.scrollBar.render(mouseX, mouseY, 0, this.x + 93, this.y + 17, 8, 142, this.scrollBarTotalHeight); - - // Render the trades - for (WidgetTradeEntry entry : this.entryList) - { - entry.render(mouseX, mouseY, currentPage == entry.getListIndex()); - } - - for (WidgetTradeEntry entry : this.entryList) - { - if (entry.isMouseOver(mouseX, mouseY)) - { - entry.postRenderHovered(mouseX, mouseY, false); - } - } - } - } - - private void reCreateEntryWidgets() - { - if (this.recipeList != null) - { - this.entryList.clear(); - - ArrayList list = new ArrayList<>(); - List favorites = this.data.getFavorites(); - - // Some favorites defined - if (favorites.isEmpty() == false) - { - // First pick all the favorited recipes, in the order they are in the favorites list - for (int index : favorites) - { - if (index >= 0 && index < this.recipeList.size()) - { - list.add(this.recipeList.get(index)); - } - } - - // Then add the rest of the recipes in their original order - for (int i = 0; i < this.recipeList.size(); ++i) - { - if (favorites.contains(i) == false) - { - list.add(this.recipeList.get(i)); - } - } - } - else - { - list.addAll(this.recipeList); - } - - final int scrollBarPos = this.scrollBar.getValue(); - final int last = Math.min(scrollBarPos + 7, list.size()); - final int x = this.x + 5; - - for (int index = scrollBarPos; index < last; ++index) - { - int y = this.y + (index - scrollBarPos) * 20 + 18; - MerchantRecipe recipe = list.get(index); - - this.entryList.add(new WidgetTradeEntry(x, y, 88, 20, recipe, this.recipeList.indexOf(recipe), this.data)); - } - } - } - - private void updateDataStorage(int currentPage) - { - int oldPosition = this.data.getTradeListPosition(); - int newPosition = this.scrollBar.getValue(); - - if (this.data.getLastPage() != currentPage) - { - this.storage.setLastPage(currentPage); - } - - if (newPosition != oldPosition) - { - this.storage.setTradeListPosition(newPosition); - this.reCreateEntryWidgets(); - } - } -} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinCraftingResultSlot.java b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinCraftingResultSlot.java new file mode 100644 index 000000000..9d28b2fad --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinCraftingResultSlot.java @@ -0,0 +1,13 @@ +package fi.dy.masa.itemscroller.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.screen.slot.CraftingResultSlot; + +@Mixin(CraftingResultSlot.class) +public interface IMixinCraftingResultSlot +{ + @Accessor("input") + CraftingInventory itemscroller_getCraftingInventory(); +} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinGuiContainer.java b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinGuiContainer.java deleted file mode 100644 index 97f35da89..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinGuiContainer.java +++ /dev/null @@ -1,33 +0,0 @@ -package fi.dy.masa.itemscroller.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.asm.mixin.gen.Invoker; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.inventory.ClickType; -import net.minecraft.inventory.Slot; - -@Mixin(GuiContainer.class) -public interface IMixinGuiContainer -{ - @Invoker("getSlotAtPosition") - Slot getSlotAtPositionInvoker(int x, int y); - - @Invoker("handleMouseClick") - void handleMouseClickInvoker(Slot slotIn, int slotId, int mouseButton, ClickType type); - - @Accessor - Slot getHoveredSlot(); - - @Accessor - int getGuiLeft(); - - @Accessor - int getGuiTop(); - - @Accessor("xSize") - int getGuiSizeX(); - - @Accessor("ySize") - int getGuiSizeY(); -} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinMerchantScreen.java b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinMerchantScreen.java new file mode 100644 index 000000000..b6b6233c7 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinMerchantScreen.java @@ -0,0 +1,12 @@ +package fi.dy.masa.itemscroller.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import net.minecraft.client.gui.screen.ingame.MerchantScreen; + +@Mixin(MerchantScreen.class) +public interface IMixinMerchantScreen +{ + @Accessor("selectedIndex") + int itemscroller_getSelectedMerchantRecipe(); +} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinScreenWithHandler.java b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinScreenWithHandler.java new file mode 100644 index 000000000..3517f3145 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinScreenWithHandler.java @@ -0,0 +1,31 @@ +package fi.dy.masa.itemscroller.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; +import net.minecraft.screen.slot.Slot; + +@Mixin(net.minecraft.client.gui.screen.ingame.HandledScreen.class) +public interface IMixinScreenWithHandler +{ + @Invoker("getSlotAt") + Slot itemscroller_getSlotAtPositionInvoker(double x, double y); + + @Invoker("onMouseClick") + void itemscroller_handleMouseClickInvoker(Slot slotIn, int slotId, int mouseButton, net.minecraft.screen.slot.SlotActionType type); + + @Accessor("focusedSlot") + Slot itemscroller_getHoveredSlot(); + + @Accessor("x") + int itemscroller_getGuiLeft(); + + @Accessor("y") + int itemscroller_getGuiTop(); + + @Accessor("backgroundWidth") + int itemscroller_getBackgroundWidth(); + + @Accessor("backgroundHeight") + int itemscroller_getBackgroundHeight(); +} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinSlot.java b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinSlot.java index 55fefa0f2..c6f6180ee 100644 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinSlot.java +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/IMixinSlot.java @@ -2,11 +2,11 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -import net.minecraft.inventory.Slot; +import net.minecraft.screen.slot.Slot; @Mixin(Slot.class) public interface IMixinSlot { - @Accessor - int getSlotIndex(); + @Accessor("index") + int itemscroller_getSlotIndex(); } diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinInventoryEffectRenderer.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinAbstractInventoryScreen.java similarity index 66% rename from src/main/java/fi/dy/masa/itemscroller/mixin/MixinInventoryEffectRenderer.java rename to src/main/java/fi/dy/masa/itemscroller/mixin/MixinAbstractInventoryScreen.java index 6f1bce48c..3cca64d2f 100644 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinInventoryEffectRenderer.java +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinAbstractInventoryScreen.java @@ -4,13 +4,13 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraft.client.gui.screen.ingame.AbstractInventoryScreen; import fi.dy.masa.itemscroller.util.InputUtils; -import net.minecraft.client.renderer.InventoryEffectRenderer; -@Mixin(InventoryEffectRenderer.class) -public abstract class MixinInventoryEffectRenderer +@Mixin(AbstractInventoryScreen.class) +public abstract class MixinAbstractInventoryScreen { - @Inject(method = "drawActivePotionEffects", at = @At("HEAD"), cancellable = true) + @Inject(method = "drawStatusEffects", at = @At("HEAD"), cancellable = true) private void preventPotionEffectRendering(CallbackInfo ci) { if (InputUtils.isRecipeViewOpen()) diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinClientPlayerInteractionManager.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinClientPlayerInteractionManager.java new file mode 100644 index 000000000..a807c8a5e --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinClientPlayerInteractionManager.java @@ -0,0 +1,37 @@ +package fi.dy.masa.itemscroller.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.network.packet.Packet; +import fi.dy.masa.itemscroller.util.ClickPacketBuffer; + +@Mixin(ClientPlayerInteractionManager.class) +public class MixinClientPlayerInteractionManager +{ + @Inject(method = "clickSlot", at = @At("HEAD"), cancellable = true) + private void cancelWindowClicksWhileReplayingBufferedPackets(CallbackInfo ci) + { + if (ClickPacketBuffer.shouldCancelWindowClicks()) + { + ci.cancel(); + } + } + + @Redirect(method = "clickSlot", at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V")) + private void bufferClickPacketsAndCancel(ClientPlayNetworkHandler netHandler, Packet packet) + { + if (ClickPacketBuffer.shouldBufferClickPackets()) + { + ClickPacketBuffer.bufferPacket(packet); + return; + } + + netHandler.sendPacket(packet); + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinContainer.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinContainer.java deleted file mode 100644 index c91d4b65a..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinContainer.java +++ /dev/null @@ -1,30 +0,0 @@ -package fi.dy.masa.itemscroller.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import fi.dy.masa.itemscroller.util.InventoryUtils; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.Container; -import net.minecraft.inventory.InventoryCraftResult; -import net.minecraft.inventory.InventoryCrafting; -import net.minecraft.world.World; - -@Mixin(Container.class) -public class MixinContainer -{ - //private static final String SCCG_SIG = "(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/inventory/InventoryCrafting;Lnet/minecraft/inventory/InventoryCraftResult;)V"; - - @Inject(method = "slotChangedCraftingGrid", at = @At("RETURN")) - public void onSlotChangedCraftingGrid( - World world, - EntityPlayer player, - InventoryCrafting inventoryCrafting, - InventoryCraftResult inventoryCraftResult, - CallbackInfo ci - ) - { - InventoryUtils.onSlotChangedCraftingGrid(world, player, inventoryCrafting, inventoryCraftResult); - } -} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinCraftingScreenHandler.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinCraftingScreenHandler.java new file mode 100644 index 000000000..8c274605b --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinCraftingScreenHandler.java @@ -0,0 +1,40 @@ +package fi.dy.masa.itemscroller.mixin; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.inventory.CraftingResultInventory; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.world.World; +import fi.dy.masa.itemscroller.util.InventoryUtils; + +@Mixin(net.minecraft.screen.CraftingScreenHandler.class) +public abstract class MixinCraftingScreenHandler +{ + @Shadow @Final private net.minecraft.inventory.CraftingInventory input; + @Shadow @Final private net.minecraft.inventory.CraftingResultInventory result; + @Shadow @Final private net.minecraft.entity.player.PlayerEntity player; + + @Inject(method = "onContentChanged", at = @At("RETURN")) + private void onSlotChangedCraftingGrid(net.minecraft.inventory.Inventory inventory, CallbackInfo ci) + { + InventoryUtils.onSlotChangedCraftingGrid(this.player, this.input, this.result); + } + + @Inject(method = "updateResult", at = @At("RETURN")) + private static void onUpdateResult( + ScreenHandler screenHandler, + World world, + PlayerEntity player, + CraftingInventory craftingInv, + CraftingResultInventory resultInv, + CallbackInfo ci) + { + InventoryUtils.onSlotChangedCraftingGrid(player, craftingInv, resultInv); + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinEntityRenderer.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinEntityRenderer.java deleted file mode 100644 index 97f53093f..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinEntityRenderer.java +++ /dev/null @@ -1,23 +0,0 @@ -package fi.dy.masa.itemscroller.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.At.Shift; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import fi.dy.masa.itemscroller.event.RenderEventHandler; -import net.minecraft.client.renderer.EntityRenderer; - -@Mixin(EntityRenderer.class) -public abstract class MixinEntityRenderer -{ - @Inject(method = "updateCameraAndRender(FJ)V", at = { - @At(value = "INVOKE", shift = Shift.AFTER, target = "Lnet/minecraftforge/client/ForgeHooksClient;drawScreen(Lnet/minecraft/client/gui/GuiScreen;IIF)V"), - @At(value = "INVOKE", shift = Shift.AFTER, target = "Lnet/minecraft/client/gui/GuiScreen;drawScreen(IIF)V") - }, - require = 1, allow = 1) - private void onDrawScreenPost(float partialTicks, long nanoTime, CallbackInfo ci) - { - RenderEventHandler.instance().onDrawScreenPost(); - } -} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGameRenderer.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGameRenderer.java new file mode 100644 index 000000000..323f66eb2 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGameRenderer.java @@ -0,0 +1,24 @@ +package fi.dy.masa.itemscroller.mixin; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import fi.dy.masa.itemscroller.event.RenderEventHandler; + +@Mixin(net.minecraft.client.render.GameRenderer.class) +public abstract class MixinGameRenderer +{ + @Shadow @Final private net.minecraft.client.MinecraftClient client; + + @Inject(method = "render(FJZ)V", + at = @At(value = "INVOKE", shift = Shift.AFTER, + target = "Lnet/minecraft/client/gui/screen/Screen;renderWithTooltip(Lnet/minecraft/client/util/math/MatrixStack;IIF)V")) + private void onDrawScreenPost(float partialTicks, long nanoTime, boolean renderWorldIn, CallbackInfo ci) + { + RenderEventHandler.instance().onDrawScreenPost(this.client); + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGuiMerchant.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGuiMerchant.java deleted file mode 100644 index 60c1305c1..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGuiMerchant.java +++ /dev/null @@ -1,105 +0,0 @@ -package fi.dy.masa.itemscroller.mixin; - -import javax.annotation.Nullable; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import fi.dy.masa.itemscroller.config.Configs; -import fi.dy.masa.itemscroller.event.InputHandler; -import fi.dy.masa.itemscroller.gui.widgets.WidgetTradeList; -import fi.dy.masa.itemscroller.util.IGuiMerchant; -import fi.dy.masa.itemscroller.villager.VillagerData; -import fi.dy.masa.itemscroller.villager.VillagerDataStorage; -import net.minecraft.client.gui.GuiMerchant; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.entity.IMerchant; -import net.minecraft.inventory.Container; -import net.minecraft.village.MerchantRecipeList; - -@Mixin(GuiMerchant.class) -public abstract class MixinGuiMerchant extends GuiContainer implements IGuiMerchant -{ - @Shadow - @Final - private IMerchant merchant; - - @Shadow - private int selectedMerchantRecipe; - - @Nullable - private WidgetTradeList widgetTradeList; - - public MixinGuiMerchant(Container inventorySlotsIn) - { - super(inventorySlotsIn); - } - - @Nullable - @Override - public WidgetTradeList getTradeListWidget() - { - return this.widgetTradeList; - } - - @Override - public int getSelectedMerchantRecipe() - { - return this.selectedMerchantRecipe; - } - - @Override - public void setSelectedMerchantRecipe(int index) - { - this.selectedMerchantRecipe = index; - } - - @Inject(method = "initGui", at = @At("RETURN")) - private void initTradeListWidget(CallbackInfo ci) - { - if (Configs.Toggles.VILLAGER_TRADE_LIST.getBooleanValue()) - { - VillagerData data = VillagerDataStorage.getInstance().getDataForLastInteractionTarget(); - - if (data != null) - { - GuiMerchant gui = (GuiMerchant) (Object) this; - - if (Configs.Generic.VILLAGER_TRADE_LIST_REMEMBER_PAGE.getBooleanValue()) - { - InputHandler.changeTradePage(gui, data.getLastPage()); - } - - int x = this.guiLeft - 106 + 4; - int y = this.guiTop; - - this.widgetTradeList = new WidgetTradeList(x, y, gui, data); - } - } - } - - @Inject(method = "drawScreen", - at = @At(value = "FIELD", - target = "Lnet/minecraft/client/gui/GuiMerchant;selectedMerchantRecipe:I"), cancellable = true) - private void recipeIndexCheck(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) - { - MerchantRecipeList trades = this.merchant.getRecipes(this.mc.player); - - if (trades != null && this.selectedMerchantRecipe >= trades.size()) - { - InputHandler.changeTradePage((GuiMerchant) (Object) this, 0); - ci.cancel(); - } - } - - @Inject(method = "drawScreen", at = @At("TAIL")) - private void onDrawScreenPost(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) - { - if (Configs.Toggles.VILLAGER_TRADE_LIST.getBooleanValue() && this.widgetTradeList != null) - { - this.widgetTradeList.render(mouseX, mouseY, false); - } - } -} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGuiScreen.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGuiScreen.java deleted file mode 100644 index 7d3c3f9b0..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinGuiScreen.java +++ /dev/null @@ -1,19 +0,0 @@ -package fi.dy.masa.itemscroller.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import fi.dy.masa.itemscroller.event.RenderEventHandler; -import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.GuiScreen; - -@Mixin(GuiScreen.class) -public abstract class MixinGuiScreen extends Gui -{ - @Inject(method = "drawDefaultBackground()V", at = @At("RETURN")) - protected void onDrawDefaultBackgroundPost(CallbackInfo ci) - { - RenderEventHandler.instance().onDrawBackgroundPost(); - } -} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinMerchantScreen.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinMerchantScreen.java new file mode 100644 index 000000000..dd79fe4cd --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinMerchantScreen.java @@ -0,0 +1,199 @@ +package fi.dy.masa.itemscroller.mixin; + +import javax.annotation.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.MerchantScreenHandler; +import net.minecraft.text.Text; +import net.minecraft.village.TradeOffer; +import fi.dy.masa.itemscroller.config.Configs; +import fi.dy.masa.itemscroller.config.Hotkeys; +import fi.dy.masa.itemscroller.gui.ItemScrollerIcons; +import fi.dy.masa.itemscroller.util.InventoryUtils; +import fi.dy.masa.itemscroller.villager.FavoriteData; +import fi.dy.masa.itemscroller.villager.IMerchantScreenHandler; +import fi.dy.masa.itemscroller.villager.VillagerData; +import fi.dy.masa.itemscroller.villager.VillagerDataStorage; +import fi.dy.masa.itemscroller.villager.VillagerUtils; +import fi.dy.masa.malilib.gui.interfaces.IGuiIcon; +import fi.dy.masa.malilib.render.RenderUtils; + +@Mixin(MerchantScreen.class) +public abstract class MixinMerchantScreen extends HandledScreen +{ + @Nullable private FavoriteData favoriteData; + @Shadow private int selectedIndex; + @Shadow int indexStartOffset; + private int indexStartOffsetLast = -1; + + @Shadow protected abstract boolean canScroll(int listSize); + + private MixinMerchantScreen(MerchantScreenHandler handler, PlayerInventory inventory, Text title) + { + super(handler, inventory, title); + } + + @Inject(method = "init", at = @At("RETURN")) + private void initTradeListWidget(CallbackInfo ci) + { + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue() && + Configs.Generic.VILLAGER_TRADE_LIST_REMEMBER_SCROLL.getBooleanValue()) + { + VillagerData data = VillagerDataStorage.getInstance().getDataForLastInteractionTarget(); + int listSize = this.handler.getRecipes().size(); + + if (data != null && this.canScroll(listSize)) + { + this.indexStartOffset = this.getClampedIndex(data.getTradeListPosition()); + } + } + } + + @Inject(method = "mouseScrolled", at = @At("RETURN")) + private void onMouseScrollPost(double mouseX, double mouseY, double amount, CallbackInfoReturnable cir) + { + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue() && + Configs.Generic.VILLAGER_TRADE_LIST_REMEMBER_SCROLL.getBooleanValue() && + this.indexStartOffsetLast != this.indexStartOffset) + { + int index = this.getClampedIndex(this.indexStartOffset); + VillagerDataStorage.getInstance().setTradeListPosition(index); + this.indexStartOffsetLast = index; + } + } + + @Inject(method = "mouseDragged", at = @At("RETURN")) + private void onMouseDragPost(double mouseX, double mouseY, int button, double deltaX, double deltaY, CallbackInfoReturnable cir) + { + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue() && + Configs.Generic.VILLAGER_TRADE_LIST_REMEMBER_SCROLL.getBooleanValue() && + this.indexStartOffsetLast != this.indexStartOffset) + { + int index = this.getClampedIndex(this.indexStartOffset); + VillagerDataStorage.getInstance().setTradeListPosition(index); + this.indexStartOffsetLast = index; + } + } + + @Inject(method = "mouseClicked", at = @At("RETURN"), cancellable = true) + private void onMouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable cir) + { + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue()) + { + int visibleIndex = this.getHoveredTradeButtonIndex(mouseX, mouseY); + int realIndex = VillagerUtils.getRealTradeIndexFor(visibleIndex, this.handler); + + if (realIndex >= 0) + { + // right click, trade everything with this trade + if (button == 1) + { + InventoryUtils.villagerTradeEverythingPossibleWithTrade(visibleIndex); + cir.setReturnValue(true); + } + // Middle click, toggle trade favorite + else if (button == 2) + { + if (Hotkeys.MODIFIER_TOGGLE_VILLAGER_GLOBAL_FAVORITE.getKeybind().isKeybindHeld()) + { + TradeOffer trade = this.handler.getRecipes().get(visibleIndex); + VillagerDataStorage.getInstance().toggleGlobalFavorite(trade); + } + else + { + VillagerDataStorage.getInstance().toggleFavorite(realIndex); + } + + this.favoriteData = null; // Force a re-build of the list + + // Rebuild the custom list based on the new favorites (See the Mixin for MerchantScreenHandler#setOffers()) + this.handler.setOffers(((IMerchantScreenHandler) this.handler).getOriginalList()); + + cir.setReturnValue(true); + } + } + } + } + + @Inject(method = "syncRecipeIndex", at = @At("HEAD"), cancellable = true) + private void fixRecipeIndex(CallbackInfo ci) + { + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue() && + this.getScreenHandler() instanceof IMerchantScreenHandler) + { + if (VillagerUtils.switchToTradeByVisibleIndex(this.selectedIndex)) + { + ci.cancel(); + } + } + } + + @Inject(method = "render", at = @At(value = "FIELD", + target = "Lnet/minecraft/client/gui/screen/ingame/MerchantScreen;offers:[Lnet/minecraft/client/gui/screen/ingame/MerchantScreen$WidgetButtonPage;")) + private void renderFavoriteMarker(MatrixStack matrices, int mouseX, int mouseY, float delta, CallbackInfo ci) + { + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue()) + { + FavoriteData favoriteData = this.favoriteData; + + if (favoriteData == null) + { + favoriteData = VillagerDataStorage.getInstance().getFavoritesForCurrentVillager(this.handler); + this.favoriteData = favoriteData; + } + + int numFavorites = favoriteData.favorites.size(); + + if (numFavorites > 0 && this.indexStartOffset < numFavorites) + { + int screenX = (this.width - this.backgroundWidth) / 2; + int screenY = (this.height - this.backgroundHeight) / 2; + int buttonsStartX = screenX + 5; + int buttonsStartY = screenY + 16 + 2; + int x = buttonsStartX + 89 - 8; + int y = buttonsStartY + 2; + float z = 300; + IGuiIcon icon = favoriteData.isGlobal ? ItemScrollerIcons.STAR_5_PURPLE : ItemScrollerIcons.STAR_5_YELLOW; + + for (int i = 0; i < (numFavorites - this.indexStartOffset); ++i) + { + RenderUtils.bindTexture(icon.getTexture()); + icon.renderAt(x, y, z, false, false); + y += 20; + } + } + } + } + + private int getClampedIndex(int index) + { + int listSize = this.handler.getRecipes().size(); + return Math.max(0, Math.min(index, listSize - 7)); + } + + private int getHoveredTradeButtonIndex(double mouseX, double mouseY) + { + int screenX = (this.width - this.backgroundWidth) / 2; + int screenY = (this.height - this.backgroundHeight) / 2; + int buttonsStartX = screenX + 5; + int buttonsStartY = screenY + 16 + 2; + int buttonWidth = 89; + int buttonHeight = 20; + + if (mouseX >= buttonsStartX && mouseX <= buttonsStartX + buttonWidth && + mouseY >= buttonsStartY && mouseY <= buttonsStartY + 7 * buttonHeight) + { + return this.indexStartOffset + (((int) mouseY - buttonsStartY) / buttonHeight); + } + + return -1; + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinMerchantScreenHandler.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinMerchantScreenHandler.java new file mode 100644 index 000000000..1033bb1a9 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinMerchantScreenHandler.java @@ -0,0 +1,54 @@ +package fi.dy.masa.itemscroller.mixin; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraft.screen.MerchantScreenHandler; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.village.Merchant; +import net.minecraft.village.TradeOfferList; +import fi.dy.masa.itemscroller.config.Configs; +import fi.dy.masa.itemscroller.villager.IMerchantScreenHandler; +import fi.dy.masa.itemscroller.villager.VillagerUtils; + +@Mixin(MerchantScreenHandler.class) +public abstract class MixinMerchantScreenHandler extends ScreenHandler implements IMerchantScreenHandler +{ + @Shadow @Final private Merchant merchant; + @Nullable private TradeOfferList customList; + + protected MixinMerchantScreenHandler(@Nullable ScreenHandlerType type, int syncId) + { + super(type, syncId); + } + + @Inject(method = "getRecipes", at = @At("HEAD"), cancellable = true) + private void replaceTradeList(CallbackInfoReturnable cir) + { + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue() && this.customList != null) + { + cir.setReturnValue(this.customList); + } + } + + @Inject(method = "setOffers", at = @At("HEAD")) + private void onTradeListSet(TradeOfferList offers, CallbackInfo ci) + { + if (Configs.Toggles.VILLAGER_TRADE_FEATURES.getBooleanValue()) + { + this.customList = VillagerUtils.buildCustomTradeList(offers); + } + } + + @Override + public TradeOfferList getOriginalList() + { + return this.merchant.getOffers(); + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/CraftingHandler.java b/src/main/java/fi/dy/masa/itemscroller/recipes/CraftingHandler.java index 223732d23..bb077b216 100644 --- a/src/main/java/fi/dy/masa/itemscroller/recipes/CraftingHandler.java +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/CraftingHandler.java @@ -5,15 +5,16 @@ import java.util.Map; import java.util.Set; import javax.annotation.Nullable; -import fi.dy.masa.itemscroller.LiteModItemScroller; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.inventory.Slot; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.Slot; +import fi.dy.masa.itemscroller.ItemScroller; public class CraftingHandler { private static final Map CRAFTING_GRID_SLOTS = new HashMap(); - private static final Set> CRAFTING_GUIS = new HashSet<>(); + private static final Set>> CRAFTING_GUIS = new HashSet<>(); public static void clearDefinitions() { @@ -26,7 +27,7 @@ public static boolean addCraftingGridDefinition(String guiClassName, String slot { try { - Class guiClass = (Class) Class.forName(guiClassName); + Class> guiClass = (Class>) Class.forName(guiClassName); Class slotClass = (Class) Class.forName(slotClassName); CRAFTING_GRID_SLOTS.put(new CraftingOutputSlot(guiClass, slotClass, outputSlot), range); @@ -36,16 +37,16 @@ public static boolean addCraftingGridDefinition(String guiClassName, String slot } catch (Exception e) { - LiteModItemScroller.logger.warn("addCraftingGridDefinition(): Failed to find classes for grid definition: gui: '{}', slot: '{}', outputSlot: {}", + ItemScroller.logger.warn("addCraftingGridDefinition(): Failed to find classes for grid definition: gui: '{}', slot: '{}', outputSlot: {}", guiClassName, slotClassName, outputSlot); } return false; } - public static boolean isCraftingGui(GuiScreen gui) + public static boolean isCraftingGui(Screen gui) { - return (gui instanceof GuiContainer) && CRAFTING_GUIS.contains(((GuiContainer) gui).getClass()); + return (gui instanceof HandledScreen) && CRAFTING_GUIS.contains(((HandledScreen) gui).getClass()); } /** @@ -55,17 +56,17 @@ public static boolean isCraftingGui(GuiScreen gui) * @return the SlotRange of the crafting grid, or null, if the given slot is not a crafting output slot */ @Nullable - public static SlotRange getCraftingGridSlots(GuiContainer gui, Slot slot) + public static SlotRange getCraftingGridSlots(HandledScreen gui, Slot slot) { return CRAFTING_GRID_SLOTS.get(CraftingOutputSlot.from(gui, slot)); } @Nullable - public static Slot getFirstCraftingOutputSlotForGui(GuiContainer gui) + public static Slot getFirstCraftingOutputSlotForGui(HandledScreen gui) { if (CRAFTING_GUIS.contains(gui.getClass())) { - for (Slot slot : gui.inventorySlots.inventorySlots) + for (Slot slot : gui.getScreenHandler().slots) { if (getCraftingGridSlots(gui, slot) != null) { @@ -79,23 +80,24 @@ public static Slot getFirstCraftingOutputSlotForGui(GuiContainer gui) public static class CraftingOutputSlot { - private final Class guiClass; + private final Class> guiClass; private final Class slotClass; private final int outputSlot; - private CraftingOutputSlot (Class guiClass, Class slotClass, int outputSlot) + private CraftingOutputSlot (Class> guiClass, Class slotClass, int outputSlot) { this.guiClass = guiClass; this.slotClass = slotClass; this.outputSlot = outputSlot; } - public static CraftingOutputSlot from(GuiContainer gui, Slot slot) + @SuppressWarnings("unchecked") + public static CraftingOutputSlot from(HandledScreen gui, Slot slot) { - return new CraftingOutputSlot(gui.getClass(), slot.getClass(), slot.slotNumber); + return new CraftingOutputSlot((Class>) gui.getClass(), slot.getClass(), slot.id); } - public Class getGuiClass() + public Class> getGuiClass() { return this.guiClass; } @@ -110,7 +112,7 @@ public int getSlotNumber() return this.outputSlot; } - public boolean matches(GuiContainer gui, Slot slot, int outputSlot) + public boolean matches(HandledScreen gui, Slot slot, int outputSlot) { return outputSlot == this.outputSlot && gui.getClass() == this.guiClass && slot.getClass() == this.slotClass; } diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/CraftingRecipe.java b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java similarity index 59% rename from src/main/java/fi/dy/masa/itemscroller/recipes/CraftingRecipe.java rename to src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java index 91aee5da6..66c0b3bd2 100644 --- a/src/main/java/fi/dy/masa/itemscroller/recipes/CraftingRecipe.java +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java @@ -1,21 +1,23 @@ package fi.dy.masa.itemscroller.recipes; +import java.util.Arrays; import javax.annotation.Nonnull; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtList; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.Slot; import fi.dy.masa.itemscroller.recipes.CraftingHandler.SlotRange; import fi.dy.masa.itemscroller.util.Constants; import fi.dy.masa.itemscroller.util.InventoryUtils; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.inventory.Slot; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -public class CraftingRecipe +public class RecipePattern { private ItemStack result = InventoryUtils.EMPTY_STACK; private ItemStack[] recipe = new ItemStack[9]; - public CraftingRecipe() + public RecipePattern() { this.ensureRecipeSizeAndClearRecipe(9); } @@ -30,11 +32,7 @@ public void ensureRecipeSize(int size) public void clearRecipe() { - for (int i = 0; i < this.recipe.length; i++) - { - this.recipe[i] = InventoryUtils.EMPTY_STACK; - } - + Arrays.fill(this.recipe, InventoryUtils.EMPTY_STACK); this.result = InventoryUtils.EMPTY_STACK; } @@ -44,23 +42,23 @@ public void ensureRecipeSizeAndClearRecipe(int size) this.clearRecipe(); } - public void storeCraftingRecipe(Slot slot, GuiContainer gui, boolean clearIfEmpty) + public void storeCraftingRecipe(Slot slot, HandledScreen gui, boolean clearIfEmpty) { SlotRange range = CraftingHandler.getCraftingGridSlots(gui, slot); if (range != null) { - if (slot.getHasStack()) + if (slot.hasStack()) { int gridSize = range.getSlotCount(); - int numSlots = gui.inventorySlots.inventorySlots.size(); + int numSlots = gui.getScreenHandler().slots.size(); this.ensureRecipeSizeAndClearRecipe(gridSize); for (int i = 0, s = range.getFirst(); i < gridSize && s < numSlots; i++, s++) { - Slot slotTmp = gui.inventorySlots.getSlot(s); - this.recipe[i] = slotTmp.getHasStack() ? slotTmp.getStack().copy() : InventoryUtils.EMPTY_STACK; + Slot slotTmp = gui.getScreenHandler().getSlot(s); + this.recipe[i] = slotTmp.hasStack() ? slotTmp.getStack().copy() : InventoryUtils.EMPTY_STACK; } this.result = slot.getStack().copy(); @@ -72,7 +70,7 @@ else if (clearIfEmpty) } } - public void copyRecipeFrom(CraftingRecipe other) + public void copyRecipeFrom(RecipePattern other) { int size = other.getRecipeLength(); ItemStack[] otherRecipe = other.getRecipeItems(); @@ -87,13 +85,13 @@ public void copyRecipeFrom(CraftingRecipe other) this.result = InventoryUtils.isStackEmpty(other.getResult()) == false ? other.getResult().copy() : InventoryUtils.EMPTY_STACK; } - public void readFromNBT(@Nonnull NBTTagCompound nbt) + public void readFromNBT(@Nonnull NbtCompound nbt) { - if (nbt.hasKey("Result", Constants.NBT.TAG_COMPOUND) && nbt.hasKey("Ingredients", Constants.NBT.TAG_LIST)) + if (nbt.contains("Result", Constants.NBT.TAG_COMPOUND) && nbt.contains("Ingredients", Constants.NBT.TAG_LIST)) { - NBTTagList tagIngredients = nbt.getTagList("Ingredients", Constants.NBT.TAG_COMPOUND); - int count = tagIngredients.tagCount(); - int length = nbt.getInteger("Length"); + NbtList tagIngredients = nbt.getList("Ingredients", Constants.NBT.TAG_COMPOUND); + int count = tagIngredients.size(); + int length = nbt.getInt("Length"); if (length > 0) { @@ -102,44 +100,44 @@ public void readFromNBT(@Nonnull NBTTagCompound nbt) for (int i = 0; i < count; i++) { - NBTTagCompound tag = tagIngredients.getCompoundTagAt(i); - int slot = tag.getInteger("Slot"); + NbtCompound tag = tagIngredients.getCompound(i); + int slot = tag.getInt("Slot"); if (slot >= 0 && slot < this.recipe.length) { - this.recipe[slot] = new ItemStack(tag); + this.recipe[slot] = ItemStack.fromNbt(tag); } } - this.result = new ItemStack(nbt.getCompoundTag("Result")); + this.result = ItemStack.fromNbt(nbt.getCompound("Result")); } } @Nonnull - public NBTTagCompound writeToNBT(@Nonnull NBTTagCompound nbt) + public NbtCompound writeToNBT(@Nonnull NbtCompound nbt) { if (this.isValid()) { - NBTTagCompound tag = new NBTTagCompound(); - this.result.writeToNBT(tag); + NbtCompound tag = new NbtCompound(); + this.result.writeNbt(tag); - nbt.setInteger("Length", this.recipe.length); - nbt.setTag("Result", tag); + nbt.putInt("Length", this.recipe.length); + nbt.put("Result", tag); - NBTTagList tagIngredients = new NBTTagList(); + NbtList tagIngredients = new NbtList(); for (int i = 0; i < this.recipe.length; i++) { if (InventoryUtils.isStackEmpty(this.recipe[i]) == false) { - tag = new NBTTagCompound(); - tag.setInteger("Slot", i); - this.recipe[i].writeToNBT(tag); - tagIngredients.appendTag(tag); + tag = new NbtCompound(); + tag.putInt("Slot", i); + this.recipe[i].writeNbt(tag); + tagIngredients.add(tag); } } - nbt.setTag("Ingredients", tagIngredients); + nbt.put("Ingredients", tagIngredients); } return nbt; diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java index 076da4792..c50f7e45f 100644 --- a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java @@ -4,23 +4,23 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import javax.annotation.Nonnull; -import fi.dy.masa.itemscroller.LiteModItemScroller; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtList; +import net.minecraft.screen.slot.Slot; +import fi.dy.masa.itemscroller.ItemScroller; import fi.dy.masa.itemscroller.Reference; import fi.dy.masa.itemscroller.config.Configs; import fi.dy.masa.itemscroller.util.Constants; import fi.dy.masa.malilib.util.FileUtils; import fi.dy.masa.malilib.util.StringUtils; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.inventory.Slot; -import net.minecraft.nbt.CompressedStreamTools; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; public class RecipeStorage { private static final RecipeStorage INSTANCE = new RecipeStorage(8 * 18); - private final CraftingRecipe[] recipes; + private final RecipePattern[] recipes; private int selected; private boolean dirty; @@ -31,7 +31,7 @@ public static RecipeStorage getInstance() public RecipeStorage(int recipeCount) { - this.recipes = new CraftingRecipe[recipeCount]; + this.recipes = new RecipePattern[recipeCount]; this.initRecipes(); } @@ -39,7 +39,7 @@ private void initRecipes() { for (int i = 0; i < this.recipes.length; i++) { - this.recipes[i] = new CraftingRecipe(); + this.recipes[i] = new RecipePattern(); } } @@ -87,7 +87,7 @@ public int getCurrentRecipePage() * If the index is invalid, then the first recipe is returned, instead of null. */ @Nonnull - public CraftingRecipe getRecipe(int index) + public RecipePattern getRecipe(int index) { if (index >= 0 && index < this.recipes.length) { @@ -98,17 +98,17 @@ public CraftingRecipe getRecipe(int index) } @Nonnull - public CraftingRecipe getSelectedRecipe() + public RecipePattern getSelectedRecipe() { return this.getRecipe(this.getSelection()); } - public void storeCraftingRecipeToCurrentSelection(Slot slot, GuiContainer gui, boolean clearIfEmpty) + public void storeCraftingRecipeToCurrentSelection(Slot slot, HandledScreen gui, boolean clearIfEmpty) { this.storeCraftingRecipe(this.getSelection(), slot, gui, clearIfEmpty); } - public void storeCraftingRecipe(int index, Slot slot, GuiContainer gui, boolean clearIfEmpty) + public void storeCraftingRecipe(int index, Slot slot, HandledScreen gui, boolean clearIfEmpty) { this.getRecipe(index).storeCraftingRecipe(slot, gui, clearIfEmpty); this.dirty = true; @@ -120,9 +120,9 @@ public void clearRecipe(int index) this.dirty = true; } - private void readFromNBT(NBTTagCompound nbt) + private void readFromNBT(NbtCompound nbt) { - if (nbt == null || nbt.hasKey("Recipes", Constants.NBT.TAG_LIST) == false) + if (nbt == null || nbt.contains("Recipes", Constants.NBT.TAG_LIST) == false) { return; } @@ -132,12 +132,12 @@ private void readFromNBT(NBTTagCompound nbt) this.recipes[i].clearRecipe(); } - NBTTagList tagList = nbt.getTagList("Recipes", Constants.NBT.TAG_COMPOUND); - int count = tagList.tagCount(); + NbtList tagList = nbt.getList("Recipes", Constants.NBT.TAG_COMPOUND); + int count = tagList.size(); for (int i = 0; i < count; i++) { - NBTTagCompound tag = tagList.getCompoundTagAt(i); + NbtCompound tag = tagList.getCompound(i); int index = tag.getByte("RecipeIndex"); @@ -150,23 +150,23 @@ private void readFromNBT(NBTTagCompound nbt) this.changeSelectedRecipe(nbt.getByte("Selected")); } - private NBTTagCompound writeToNBT(@Nonnull NBTTagCompound nbt) + private NbtCompound writeToNBT(@Nonnull NbtCompound nbt) { - NBTTagList tagRecipes = new NBTTagList(); + NbtList tagRecipes = new NbtList(); for (int i = 0; i < this.recipes.length; i++) { if (this.recipes[i].isValid()) { - NBTTagCompound tag = new NBTTagCompound(); - tag.setByte("RecipeIndex", (byte) i); + NbtCompound tag = new NbtCompound(); + tag.putByte("RecipeIndex", (byte) i); this.recipes[i].writeToNBT(tag); - tagRecipes.appendTag(tag); + tagRecipes.add(tag); } } - nbt.setTag("Recipes", tagRecipes); - nbt.setByte("Selected", (byte) this.selected); + nbt.put("Recipes", tagRecipes); + nbt.putByte("Selected", (byte) this.selected); return nbt; } @@ -204,7 +204,7 @@ public void readFromDisk() if (file.exists() && file.isFile() && file.canRead()) { FileInputStream is = new FileInputStream(file); - this.readFromNBT(CompressedStreamTools.readCompressed(is)); + this.readFromNBT(NbtIo.readCompressed(is)); is.close(); //ItemScroller.logger.info("Read recipes from file '{}'", file.getPath()); } @@ -212,7 +212,7 @@ public void readFromDisk() } catch (Exception e) { - LiteModItemScroller.logger.warn("Failed to read recipes from file", e); + ItemScroller.logger.warn("Failed to read recipes from file", e); } } @@ -224,16 +224,11 @@ public void writeToDisk() { File saveDir = this.getSaveDir(); - if (saveDir == null) - { - return; - } - if (saveDir.exists() == false) { if (saveDir.mkdirs() == false) { - LiteModItemScroller.logger.warn("Failed to create the recipe storage directory '{}'", saveDir.getPath()); + ItemScroller.logger.warn("Failed to create the recipe storage directory '{}'", saveDir.getPath()); return; } } @@ -241,7 +236,7 @@ public void writeToDisk() File fileTmp = new File(saveDir, this.getFileName() + ".tmp"); File fileReal = new File(saveDir, this.getFileName()); FileOutputStream os = new FileOutputStream(fileTmp); - CompressedStreamTools.writeCompressed(this.writeToNBT(new NBTTagCompound()), os); + NbtIo.writeCompressed(this.writeToNBT(new NbtCompound()), os); os.close(); if (fileReal.exists()) @@ -254,7 +249,7 @@ public void writeToDisk() } catch (Exception e) { - LiteModItemScroller.logger.warn("Failed to write recipes to file!", e); + ItemScroller.logger.warn("Failed to write recipes to file!", e); } } } diff --git a/src/main/java/fi/dy/masa/itemscroller/util/AccessorUtils.java b/src/main/java/fi/dy/masa/itemscroller/util/AccessorUtils.java index ee4b85ad1..45185423e 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/AccessorUtils.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/AccessorUtils.java @@ -1,56 +1,60 @@ package fi.dy.masa.itemscroller.util; -import fi.dy.masa.itemscroller.mixin.IMixinGuiContainer; +import javax.annotation.Nullable; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.screen.slot.Slot; +import net.minecraft.screen.slot.SlotActionType; +import fi.dy.masa.itemscroller.mixin.IMixinMerchantScreen; +import fi.dy.masa.itemscroller.mixin.IMixinScreenWithHandler; import fi.dy.masa.itemscroller.mixin.IMixinSlot; -import net.minecraft.client.gui.GuiMerchant; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.inventory.ClickType; -import net.minecraft.inventory.Slot; public class AccessorUtils { - public static Slot getSlotUnderMouse(GuiContainer gui) + @Nullable + public static Slot getSlotUnderMouse(HandledScreen gui) { - return ((IMixinGuiContainer) gui).getHoveredSlot(); + return ((IMixinScreenWithHandler) gui).itemscroller_getHoveredSlot(); } - public static Slot getSlotAtPosition(GuiContainer gui, int x, int y) + @Nullable + public static Slot getSlotAtPosition(HandledScreen gui, int x, int y) { - return ((IMixinGuiContainer) gui).getSlotAtPositionInvoker(x, y); + return ((IMixinScreenWithHandler) gui).itemscroller_getSlotAtPositionInvoker(x, y); } - public static void handleMouseClick(GuiContainer gui, Slot slotIn, int slotId, int mouseButton, ClickType type) + public static void handleMouseClick(HandledScreen gui, Slot slotIn, int slotId, int mouseButton, SlotActionType type) { - ((IMixinGuiContainer) gui).handleMouseClickInvoker(slotIn, slotId, mouseButton, type); + ((IMixinScreenWithHandler) gui).itemscroller_handleMouseClickInvoker(slotIn, slotId, mouseButton, type); } - public static int getGuiLeft(GuiContainer gui) + public static int getGuiLeft(HandledScreen gui) { - return ((IMixinGuiContainer) gui).getGuiLeft(); + return ((IMixinScreenWithHandler) gui).itemscroller_getGuiLeft(); } - public static int getGuiTop(GuiContainer gui) + public static int getGuiTop(HandledScreen gui) { - return ((IMixinGuiContainer) gui).getGuiTop(); + return ((IMixinScreenWithHandler) gui).itemscroller_getGuiTop(); } - public static int getGuiXSize(GuiContainer gui) + public static int getGuiXSize(HandledScreen gui) { - return ((IMixinGuiContainer) gui).getGuiSizeX(); + return ((IMixinScreenWithHandler) gui).itemscroller_getBackgroundWidth(); } - public static int getGuiYSize(GuiContainer gui) + public static int getGuiYSize(HandledScreen gui) { - return ((IMixinGuiContainer) gui).getGuiSizeY(); + return ((IMixinScreenWithHandler) gui).itemscroller_getBackgroundHeight(); } - public static int getSelectedMerchantRecipe(GuiMerchant gui) + public static int getSelectedMerchantRecipe(MerchantScreen gui) { - return ((IGuiMerchant) gui).getSelectedMerchantRecipe(); + return ((IMixinMerchantScreen) gui).itemscroller_getSelectedMerchantRecipe(); } public static int getSlotIndex(Slot slot) { - return ((IMixinSlot) slot).getSlotIndex(); + return ((IMixinSlot) slot).itemscroller_getSlotIndex(); } } diff --git a/src/main/java/fi/dy/masa/itemscroller/util/ClickPacketBuffer.java b/src/main/java/fi/dy/masa/itemscroller/util/ClickPacketBuffer.java new file mode 100644 index 000000000..fa05b38b9 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/util/ClickPacketBuffer.java @@ -0,0 +1,71 @@ +package fi.dy.masa.itemscroller.util; + +import java.util.ArrayDeque; +import java.util.Queue; +import net.minecraft.client.MinecraftClient; +import net.minecraft.network.packet.Packet; + +public class ClickPacketBuffer +{ + private static final Queue> BUFFER = new ArrayDeque<>(2048); + private static boolean shouldBufferPackets; + private static boolean hasBufferedPackets; + + public static void reset() + { + shouldBufferPackets = false; + hasBufferedPackets = false; + BUFFER.clear(); + } + + public static int getBufferedActionsCount() + { + return BUFFER.size(); + } + + public static boolean shouldBufferClickPackets() + { + return shouldBufferPackets; + } + + public static boolean shouldCancelWindowClicks() + { + // Don't cancel the clicks on the client if we have some Item Scroller actions in progress + return shouldBufferPackets == false && BUFFER.isEmpty() == false; + } + + public static void setShouldBufferClickPackets(boolean shouldBuffer) + { + shouldBufferPackets = shouldBuffer; + } + + public static void bufferPacket(Packet packet) + { + BUFFER.offer(packet); + hasBufferedPackets = true; + } + + public static void sendBufferedPackets(int maxCount) + { + MinecraftClient mc = MinecraftClient.getInstance(); + + if (hasBufferedPackets) + { + if (mc.currentScreen == null) + { + reset(); + } + else if (mc.player != null) + { + maxCount = Math.min(maxCount, BUFFER.size()); + + for (int i = 0; i < maxCount; ++i) + { + mc.player.networkHandler.sendPacket(BUFFER.poll()); + } + + hasBufferedPackets = BUFFER.isEmpty() == false; + } + } + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/util/IGuiMerchant.java b/src/main/java/fi/dy/masa/itemscroller/util/IGuiMerchant.java deleted file mode 100644 index 20351ac06..000000000 --- a/src/main/java/fi/dy/masa/itemscroller/util/IGuiMerchant.java +++ /dev/null @@ -1,14 +0,0 @@ -package fi.dy.masa.itemscroller.util; - -import javax.annotation.Nullable; -import fi.dy.masa.itemscroller.gui.widgets.WidgetTradeList; - -public interface IGuiMerchant -{ - int getSelectedMerchantRecipe(); - - void setSelectedMerchantRecipe(int index); - - @Nullable - WidgetTradeList getTradeListWidget(); -} diff --git a/src/main/java/fi/dy/masa/itemscroller/util/InputUtils.java b/src/main/java/fi/dy/masa/itemscroller/util/InputUtils.java index 8052b452c..c955513c4 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/InputUtils.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/InputUtils.java @@ -1,49 +1,35 @@ package fi.dy.masa.itemscroller.util; -import org.lwjgl.input.Mouse; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.HandledScreen; import fi.dy.masa.itemscroller.config.Hotkeys; import fi.dy.masa.itemscroller.event.KeybindCallbacks; import fi.dy.masa.itemscroller.recipes.CraftingHandler; import fi.dy.masa.malilib.hotkeys.IKeybind; +import fi.dy.masa.malilib.hotkeys.KeybindMulti; import fi.dy.masa.malilib.util.GuiUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.inventory.GuiContainer; public class InputUtils { - public static int getMouseX() - { - int width = GuiUtils.getScaledWindowWidth(); - return Mouse.getX() * width / GuiUtils.getDisplayWidth(); - } - - public static int getMouseY() - { - int height = GuiUtils.getScaledWindowHeight(); - return height - Mouse.getY() * height / GuiUtils.getDisplayHeight() - 1; - } - public static boolean isRecipeViewOpen() { return GuiUtils.getCurrentScreen() != null && - Hotkeys.KEY_RECIPE_VIEW.getKeybind().isKeybindHeld() && + Hotkeys.RECIPE_VIEW.getKeybind().isKeybindHeld() && KeybindCallbacks.getInstance().functionalityEnabled() && CraftingHandler.isCraftingGui(GuiUtils.getCurrentScreen()); } - public static boolean canShiftDropItems(GuiContainer gui, Minecraft mc) + public static boolean canShiftDropItems(HandledScreen gui, MinecraftClient mc, int mouseX, int mouseY) { - if (InventoryUtils.isStackEmpty(mc.player.inventory.getItemStack()) == false) + if (InventoryUtils.isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { int left = AccessorUtils.getGuiLeft(gui); int top = AccessorUtils.getGuiTop(gui); int xSize = AccessorUtils.getGuiXSize(gui); int ySize = AccessorUtils.getGuiYSize(gui); - int mouseAbsX = Mouse.getEventX() * gui.width / GuiUtils.getDisplayWidth(); - int mouseAbsY = gui.height - Mouse.getEventY() * gui.height / GuiUtils.getDisplayHeight() - 1; - boolean isOutsideGui = mouseAbsX < left || mouseAbsY < top || mouseAbsX >= left + xSize || mouseAbsY >= top + ySize; + boolean isOutsideGui = mouseX < left || mouseY < top || mouseX >= left + xSize || mouseY >= top + ySize; - return isOutsideGui && AccessorUtils.getSlotAtPosition(gui, mouseAbsX - left, mouseAbsY - top) == null; + return isOutsideGui && AccessorUtils.getSlotAtPosition(gui, mouseX - left, mouseY - top) == null; } return false; @@ -141,16 +127,16 @@ public static MoveAmount getMoveAmount(MoveAction action) public static boolean isAttack(int keyCode) { - return keyCode == Minecraft.getMinecraft().gameSettings.keyBindAttack.getKeyCode(); + return keyCode == KeybindMulti.getKeyCode(MinecraftClient.getInstance().options.attackKey); } public static boolean isUse(int keyCode) { - return keyCode == Minecraft.getMinecraft().gameSettings.keyBindUseItem.getKeyCode(); + return keyCode == KeybindMulti.getKeyCode(MinecraftClient.getInstance().options.useKey); } public static boolean isPickBlock(int keyCode) { - return keyCode == Minecraft.getMinecraft().gameSettings.keyBindPickBlock.getKeyCode(); + return keyCode == KeybindMulti.getKeyCode(MinecraftClient.getInstance().options.pickItemKey); } } diff --git a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java index a6d58f0a9..6863deffa 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java @@ -1,80 +1,140 @@ package fi.dy.masa.itemscroller.util; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; -import fi.dy.masa.itemscroller.LiteModItemScroller; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntComparator; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.inventory.CraftingResultInventory; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.recipe.CraftingRecipe; +import net.minecraft.recipe.RecipeType; +import net.minecraft.registry.Registries; +import net.minecraft.screen.MerchantScreenHandler; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.CraftingResultSlot; +import net.minecraft.screen.slot.Slot; +import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.screen.slot.TradeOutputSlot; +import net.minecraft.util.Identifier; +import net.minecraft.village.TradeOffer; +import net.minecraft.village.TradeOfferList; +import net.minecraft.world.GameRules; +import net.minecraft.world.World; + +import fi.dy.masa.itemscroller.ItemScroller; import fi.dy.masa.itemscroller.config.Configs; import fi.dy.masa.itemscroller.config.Hotkeys; -import fi.dy.masa.itemscroller.event.InputHandler; +import fi.dy.masa.itemscroller.mixin.IMixinCraftingResultSlot; import fi.dy.masa.itemscroller.recipes.CraftingHandler; import fi.dy.masa.itemscroller.recipes.CraftingHandler.SlotRange; -import fi.dy.masa.itemscroller.recipes.CraftingRecipe; +import fi.dy.masa.itemscroller.recipes.RecipePattern; import fi.dy.masa.itemscroller.recipes.RecipeStorage; -import fi.dy.masa.itemscroller.villager.VillagerData; import fi.dy.masa.itemscroller.villager.VillagerDataStorage; +import fi.dy.masa.itemscroller.villager.VillagerUtils; import fi.dy.masa.malilib.util.GuiUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.client.gui.GuiMerchant; -import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.gui.inventory.GuiContainerCreative; -import net.minecraft.client.gui.inventory.GuiInventory; -import net.minecraft.creativetab.CreativeTabs; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.inventory.ClickType; -import net.minecraft.inventory.Container; -import net.minecraft.inventory.IInventory; -import net.minecraft.inventory.InventoryCraftResult; -import net.minecraft.inventory.InventoryCrafting; -import net.minecraft.inventory.Slot; -import net.minecraft.inventory.SlotMerchantResult; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.item.crafting.CraftingManager; -import net.minecraft.item.crafting.IRecipe; -import net.minecraft.util.ResourceLocation; -import net.minecraft.village.MerchantRecipe; -import net.minecraft.village.MerchantRecipeList; -import net.minecraft.world.World; public class InventoryUtils { + private static final Set DRAGGED_SLOTS = new HashSet<>(); + + private static WeakReference sourceSlotCandidate = null; + private static WeakReference sourceSlot = null; + private static ItemStack stackInCursorLast = ItemStack.EMPTY; + @Nullable protected static CraftingRecipe lastRecipe; private static MoveAction activeMoveAction = MoveAction.NONE; private static int lastPosX; private static int lastPosY; private static int slotNumberLast; - private static final Set DRAGGED_SLOTS = new HashSet(); - private static WeakReference sourceSlotCandidate = null; - private static WeakReference sourceSlot = null; - private static ItemStack stackInCursorLast = ItemStack.EMPTY; + private static boolean inhibitCraftResultUpdate; + + public static void setInhibitCraftingOutputUpdate(boolean inhibitUpdate) + { + inhibitCraftResultUpdate = inhibitUpdate; + } + + public static void onSlotChangedCraftingGrid(PlayerEntity player, + CraftingInventory craftMatrix, + CraftingResultInventory inventoryCraftResult) + { + if (inhibitCraftResultUpdate && Configs.Generic.MASS_CRAFT_INHIBIT_MID_UPDATES.getBooleanValue()) + { + return; + } - public static void onSlotChangedCraftingGrid(World world, EntityPlayer player, InventoryCrafting inventoryCrafting, InventoryCraftResult inventoryCraftResult) + if (Configs.Generic.CLIENT_CRAFTING_FIX.getBooleanValue()) + { + updateCraftingOutputSlot(player, craftMatrix, inventoryCraftResult, true); + } + } + + public static void updateCraftingOutputSlot(Slot outputSlot) + { + PlayerEntity player = MinecraftClient.getInstance().player; + + if (player != null && + outputSlot instanceof CraftingResultSlot resultSlot && + resultSlot.inventory instanceof CraftingResultInventory resultInv) + { + CraftingInventory craftingInv = ((IMixinCraftingResultSlot) outputSlot).itemscroller_getCraftingInventory(); + updateCraftingOutputSlot(player, craftingInv, resultInv, true); + } + } + + public static void updateCraftingOutputSlot(PlayerEntity player, + CraftingInventory craftMatrix, + CraftingResultInventory inventoryCraftResult, + boolean setEmptyStack) { - if (Configs.Generic.CLIENT_CRAFTING_FIX.getBooleanValue() && - world.isRemote && player instanceof EntityPlayerSP) + World world = player.getEntityWorld(); + + if ((world instanceof ClientWorld) && player instanceof ClientPlayerEntity) { ItemStack stack = ItemStack.EMPTY; - IRecipe recipe = CraftingManager.findMatchingRecipe(inventoryCrafting, world); + CraftingRecipe recipe = Configs.Generic.USE_RECIPE_CACHING.getBooleanValue() ? lastRecipe : null; - if (recipe != null && - (recipe.isDynamic() || - world.getGameRules().getBoolean("doLimitedCrafting") == false || - ((EntityPlayerSP) player).getRecipeBook().isUnlocked(recipe)) - ) + if (recipe == null || recipe.matches(craftMatrix, world) == false) { - inventoryCraftResult.setRecipeUsed(recipe); - stack = recipe.getCraftingResult(inventoryCrafting); + Optional optional = world.getRecipeManager().getFirstMatch(RecipeType.CRAFTING, craftMatrix, world); + recipe = optional.isPresent() ? optional.get() : null; + } + + if (recipe != null) + { + if ((recipe.isIgnoredInRecipeBook() || + world.getGameRules().getBoolean(GameRules.DO_LIMITED_CRAFTING) == false || + ((ClientPlayerEntity) player).getRecipeBook().contains(recipe))) + { + inventoryCraftResult.setLastRecipe(recipe); + stack = recipe.craft(craftMatrix, MinecraftClient.getInstance().getNetworkHandler().getRegistryManager()); + } + + if (setEmptyStack || stack.isEmpty() == false) + { + inventoryCraftResult.setStack(0, stack); + } + } - inventoryCraftResult.setInventorySlotContents(0, stack); + lastRecipe = recipe; } } @@ -82,45 +142,47 @@ public static String getStackString(ItemStack stack) { if (isStackEmpty(stack) == false) { - ResourceLocation rl = Item.REGISTRY.getNameForObject(stack.getItem()); + Identifier rl = Registries.ITEM.getId(stack.getItem()); + String idStr = rl != null ? rl.toString() : "null"; + String displayName = stack.getName().getString(); + String nbtStr = stack.getNbt() != null ? stack.getNbt().toString() : ""; - return String.format("[%s @ %d - display: %s - NBT: %s] (%s)", - rl != null ? rl.toString() : "null", stack.getMetadata(), stack.getDisplayName(), - stack.getTagCompound() != null ? stack.getTagCompound().toString() : "", - stack.toString()); + return String.format("[%s - display: %s - NBT: %s] (%s)", idStr, displayName, nbtStr, stack); } return ""; } - public static void debugPrintSlotInfo(GuiContainer gui, Slot slot) + public static void debugPrintSlotInfo(HandledScreen gui, Slot slot) { if (slot == null) { - LiteModItemScroller.logger.info("slot was null"); + ItemScroller.logger.info("slot was null"); return; } - boolean hasSlot = gui.inventorySlots.inventorySlots.contains(slot); + boolean hasSlot = gui.getScreenHandler().slots.contains(slot); Object inv = slot.inventory; String stackStr = InventoryUtils.getStackString(slot.getStack()); - LiteModItemScroller.logger.info(String.format("slot: slotNumber: %d, getSlotIndex(): %d, getHasStack(): %s, " + + ItemScroller.logger.info(String.format("slot: slotNumber: %d, getSlotIndex(): %d, getHasStack(): %s, " + "slot class: %s, inv class: %s, Container's slot list has slot: %s, stack: %s, numSlots: %d", - slot.slotNumber, AccessorUtils.getSlotIndex(slot), slot.getHasStack(), slot.getClass().getName(), + slot.id, AccessorUtils.getSlotIndex(slot), slot.hasStack(), slot.getClass().getName(), inv != null ? inv.getClass().getName() : "", hasSlot ? " true" : "false", stackStr, - gui.inventorySlots.inventorySlots.size())); + gui.getScreenHandler().slots.size())); } - private static boolean isValidSlot(Slot slot, GuiContainer gui, boolean requireItems) + private static boolean isValidSlot(Slot slot, HandledScreen gui, boolean requireItems) { - return gui.inventorySlots != null && gui.inventorySlots.inventorySlots != null && - slot != null && gui.inventorySlots.inventorySlots.contains(slot) && - (requireItems == false || slot.getHasStack()) && + ScreenHandler container = gui.getScreenHandler(); + + return container != null && container.slots != null && + slot != null && container.slots.contains(slot) && + (requireItems == false || slot.hasStack()) && Configs.SLOT_BLACKLIST.contains(slot.getClass().getName()) == false; } - public static boolean isCraftingSlot(GuiContainer gui, @Nullable Slot slot) + public static boolean isCraftingSlot(HandledScreen gui, @Nullable Slot slot) { return slot != null && CraftingHandler.getCraftingGridSlots(gui, slot) != null; } @@ -128,11 +190,11 @@ public static boolean isCraftingSlot(GuiContainer gui, @Nullable Slot slot) /** * Checks if there are slots belonging to another inventory on screen above the given slot */ - private static boolean inventoryExistsAbove(Slot slot, Container container) + private static boolean inventoryExistsAbove(Slot slot, ScreenHandler container) { - for (Slot slotTmp : container.inventorySlots) + for (Slot slotTmp : container.slots) { - if (slotTmp.yPos < slot.yPos && areSlotsInSameInventory(slot, slotTmp) == false) + if (slotTmp.y < slot.y && areSlotsInSameInventory(slot, slotTmp) == false) { return true; } @@ -141,30 +203,30 @@ private static boolean inventoryExistsAbove(Slot slot, Container container) return false; } - public static boolean canShiftPlaceItems(GuiContainer gui) + public static boolean canShiftPlaceItems(HandledScreen gui) { Slot slot = AccessorUtils.getSlotUnderMouse(gui); - Minecraft mc = Minecraft.getMinecraft(); - ItemStack stackCursor = mc.player.inventory.getItemStack(); + ItemStack stackCursor = gui.getScreenHandler().getCursorStack(); // The target slot needs to be an empty, valid slot, and there needs to be items in the cursor return slot != null && isStackEmpty(stackCursor) == false && isValidSlot(slot, gui, false) && - slot.getHasStack() == false && slot.isItemValid(stackCursor); + slot.hasStack() == false && slot.canInsert(stackCursor); } - public static boolean tryMoveItems(GuiContainer gui, RecipeStorage recipes, boolean scrollingUp) + public static boolean tryMoveItems(HandledScreen gui, + RecipeStorage recipes, + boolean scrollingUp) { Slot slot = AccessorUtils.getSlotUnderMouse(gui); - Minecraft mc = Minecraft.getMinecraft(); // We require an empty cursor - if (slot == null || isStackEmpty(mc.player.inventory.getItemStack()) == false) + if (slot == null || isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { return false; } // Villager handling only happens when scrolling over the trade output slot - boolean villagerHandling = Configs.Toggles.SCROLL_VILLAGER.getBooleanValue() && gui instanceof GuiMerchant && slot instanceof SlotMerchantResult; + boolean villagerHandling = Configs.Toggles.SCROLL_VILLAGER.getBooleanValue() && gui instanceof MerchantScreen && slot instanceof TradeOutputSlot; boolean craftingHandling = Configs.Toggles.CRAFTING_FEATURES.getBooleanValue() && isCraftingSlot(gui, slot); boolean keyActiveMoveEverything = Hotkeys.MODIFIER_MOVE_EVERYTHING.getKeybind().isKeybindHeld(); boolean keyActiveMoveMatching = Hotkeys.MODIFIER_MOVE_MATCHING.getKeybind().isKeybindHeld(); @@ -174,7 +236,7 @@ public static boolean tryMoveItems(GuiContainer gui, RecipeStorage recipes, bool if (Configs.Generic.SLOT_POSITION_AWARE_SCROLL_DIRECTION.getBooleanValue()) { - boolean above = inventoryExistsAbove(slot, gui.inventorySlots); + boolean above = inventoryExistsAbove(slot, gui.getScreenHandler()); // so basically: (above && scrollingUp) || (above == false && scrollingUp == false) moveToOtherInventory = (above == scrollingUp); } @@ -186,7 +248,7 @@ public static boolean tryMoveItems(GuiContainer gui, RecipeStorage recipes, bool } // Check that the slot is valid, (don't require items in case of the villager output slot or a crafting slot) - if (isValidSlot(slot, gui, villagerHandling || craftingHandling ? false : true) == false) + if (isValidSlot(slot, gui, villagerHandling == false && craftingHandling == false) == false) { return false; } @@ -198,7 +260,7 @@ public static boolean tryMoveItems(GuiContainer gui, RecipeStorage recipes, bool if (villagerHandling) { - return tryMoveItemsVillager((GuiMerchant) gui, slot, moveToOtherInventory, keyActiveMoveStacks); + return tryMoveItemsVillager((MerchantScreen) gui, slot, moveToOtherInventory, keyActiveMoveStacks); } if ((Configs.Toggles.SCROLL_SINGLE.getBooleanValue() == false && nonSingleMove == false) || @@ -235,7 +297,7 @@ else if (keyActiveMoveStacks) tryMoveSingleItemToOtherInventory(slot, gui); } // Scrolling items from the other inventory into this slot/inventory - else if (getStackSize(stack) < slot.getItemStackLimit(stack)) + else if (getStackSize(stack) < slot.getMaxItemCount(stack)) { tryMoveSingleItemToThisInventory(slot, gui); } @@ -244,12 +306,11 @@ else if (getStackSize(stack) < slot.getItemStackLimit(stack)) return false; } - public static boolean dragMoveItems(GuiContainer gui, Minecraft mc, MoveAction action, boolean isClick) + public static boolean dragMoveItems(HandledScreen gui, + MoveAction action, + int mouseX, int mouseY, boolean isClick) { - int mouseX = InputUtils.getMouseX(); - int mouseY = InputUtils.getMouseY(); - - if (isStackEmpty(mc.player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { // Updating these here is part of the fix to preventing a drag after shift + place lastPosX = mouseX; @@ -325,15 +386,15 @@ public static boolean dragMoveItems(GuiContainer gui, Minecraft mc, MoveAction a if (slot != null) { - if (gui instanceof GuiContainerCreative) + if (gui instanceof CreativeInventoryScreen) { - boolean isPlayerInv = ((GuiContainerCreative) gui).getSelectedTabIndex() == CreativeTabs.INVENTORY.getIndex(); - int slotNumber = isPlayerInv ? AccessorUtils.getSlotIndex(slot) : slot.slotNumber; + boolean isPlayerInv = ((CreativeInventoryScreen) gui).isInventoryTabSelected(); // TODO 1.19.3+ + int slotNumber = isPlayerInv ? AccessorUtils.getSlotIndex(slot) : slot.id; slotNumberLast = slotNumber; } else { - slotNumberLast = slot.slotNumber; + slotNumberLast = slot.id; } } else @@ -350,21 +411,22 @@ public static void stopDragging() DRAGGED_SLOTS.clear(); } - private static boolean dragMoveFromSlotAtPosition(GuiContainer gui, int x, int y, MoveAction action) + private static boolean dragMoveFromSlotAtPosition(HandledScreen gui, + int x, int y, MoveAction action) { - if (gui instanceof GuiContainerCreative) + if (gui instanceof CreativeInventoryScreen) { return dragMoveFromSlotAtPositionCreative(gui, x, y, action); } Slot slot = AccessorUtils.getSlotAtPosition(gui, x, y); - Minecraft mc = Minecraft.getMinecraft(); + MinecraftClient mc = MinecraftClient.getInstance(); MoveAmount amount = InputUtils.getMoveAmount(action); - boolean flag = slot != null && isValidSlot(slot, gui, true) && slot.canTakeStack(mc.player); + boolean flag = slot != null && isValidSlot(slot, gui, true) && slot.canTakeItems(mc.player); //boolean cancel = flag && (amount == MoveAmount.LEAVE_ONE || amount == MoveAmount.MOVE_ONE); - if (flag && slot.slotNumber != slotNumberLast && - (amount != MoveAmount.MOVE_ONE || DRAGGED_SLOTS.contains(slot.slotNumber) == false)) + if (flag && slot.id != slotNumberLast && + (amount != MoveAmount.MOVE_ONE || DRAGGED_SLOTS.contains(slot.id) == false)) { switch (action) { @@ -377,7 +439,7 @@ private static boolean dragMoveFromSlotAtPosition(GuiContainer gui, int x, int y break; case MOVE_TO_OTHER_STACKS: - shiftClickSlot(gui, slot.slotNumber); + shiftClickSlot(gui, slot.id); break; case MOVE_TO_OTHER_MATCHING: @@ -385,17 +447,17 @@ private static boolean dragMoveFromSlotAtPosition(GuiContainer gui, int x, int y break; case DROP_ONE: - clickSlot(gui, slot.slotNumber, 0, ClickType.THROW); + clickSlot(gui, slot.id, 0, SlotActionType.THROW); break; case DROP_LEAVE_ONE: - leftClickSlot(gui, slot.slotNumber); - rightClickSlot(gui, slot.slotNumber); + leftClickSlot(gui, slot.id); + rightClickSlot(gui, slot.id); dropItemsFromCursor(gui); break; case DROP_STACKS: - clickSlot(gui, slot.slotNumber, 1, ClickType.THROW); + clickSlot(gui, slot.id, 1, SlotActionType.THROW); break; case MOVE_DOWN_MOVE_ONE: @@ -415,17 +477,18 @@ private static boolean dragMoveFromSlotAtPosition(GuiContainer gui, int x, int y default: } - DRAGGED_SLOTS.add(slot.slotNumber); + DRAGGED_SLOTS.add(slot.id); } return true; } - private static boolean dragMoveFromSlotAtPositionCreative(GuiContainer gui, int x, int y, MoveAction action) + private static boolean dragMoveFromSlotAtPositionCreative(HandledScreen gui, + int x, int y, MoveAction action) { - GuiContainerCreative guiCreative = (GuiContainerCreative) gui; + CreativeInventoryScreen guiCreative = (CreativeInventoryScreen) gui; Slot slot = AccessorUtils.getSlotAtPosition(gui, x, y); - boolean isPlayerInv = guiCreative.getSelectedTabIndex() == CreativeTabs.INVENTORY.getIndex(); + boolean isPlayerInv = guiCreative.isInventoryTabSelected(); // TODO 1.19.3+ // Only allow dragging from the hotbar slots if (slot == null || (slot.getClass() != Slot.class && isPlayerInv == false)) @@ -433,14 +496,14 @@ private static boolean dragMoveFromSlotAtPositionCreative(GuiContainer gui, int return false; } - Minecraft mc = Minecraft.getMinecraft(); + MinecraftClient mc = MinecraftClient.getInstance(); MoveAmount amount = InputUtils.getMoveAmount(action); - boolean flag = slot != null && isValidSlot(slot, gui, true) && slot.canTakeStack(mc.player); + boolean flag = slot != null && isValidSlot(slot, gui, true) && slot.canTakeItems(mc.player); boolean cancel = flag && (amount == MoveAmount.LEAVE_ONE || amount == MoveAmount.MOVE_ONE); // The player inventory tab of the creative inventory uses stupid wrapped // slots that all have slotNumber = 0 on the outer instance ;_; // However in that case we can use the slotIndex which is easy enough to get. - int slotNumber = isPlayerInv ? AccessorUtils.getSlotIndex(slot) : slot.slotNumber; + int slotNumber = isPlayerInv ? AccessorUtils.getSlotIndex(slot) : slot.id; if (flag && slotNumber != slotNumberLast && DRAGGED_SLOTS.contains(slotNumber) == false) { @@ -464,8 +527,8 @@ private static boolean dragMoveFromSlotAtPositionCreative(GuiContainer gui, int rightClickSlot(guiCreative, slot, slotNumber); // Delete the rest of the stack by placing it in the first creative "source slot" - Slot slotFirst = gui.inventorySlots.inventorySlots.get(0); - leftClickSlot(guiCreative, slotFirst, slotFirst.slotNumber); + Slot slotFirst = gui.getScreenHandler().slots.get(0); + leftClickSlot(guiCreative, slotFirst, slotFirst.id); } cancel = true; @@ -478,17 +541,17 @@ private static boolean dragMoveFromSlotAtPositionCreative(GuiContainer gui, int break; case DROP_ONE: - clickSlot(gui, slot.slotNumber, 0, ClickType.THROW); + clickSlot(gui, slot.id, 0, SlotActionType.THROW); break; case DROP_LEAVE_ONE: - leftClickSlot(gui, slot.slotNumber); - rightClickSlot(gui, slot.slotNumber); + leftClickSlot(gui, slot.id); + rightClickSlot(gui, slot.id); dropItemsFromCursor(gui); break; case DROP_STACKS: - clickSlot(gui, slot.slotNumber, 1, ClickType.THROW); + clickSlot(gui, slot.id, 1, SlotActionType.THROW); cancel = true; break; @@ -515,29 +578,52 @@ private static boolean dragMoveFromSlotAtPositionCreative(GuiContainer gui, int return cancel; } - public static void dropStacks(GuiContainer gui, ItemStack stackReference, Slot slotReference, boolean sameInventory) + public static void dropStacks(HandledScreen gui, + ItemStack stackReference, + Slot slotReference, + boolean sameInventory) { if (slotReference != null && isStackEmpty(stackReference) == false) { - Container container = gui.inventorySlots; + ScreenHandler container = gui.getScreenHandler(); stackReference = stackReference.copy(); - for (Slot slot : container.inventorySlots) + for (Slot slot : container.slots) { // If this slot is in the same inventory that the items were picked up to the cursor from // and the stack is identical to the one in the cursor, then this stack will get dropped. - if (areSlotsInSameInventory(slot, slotReference) == sameInventory && areStacksEqual(slot.getStack(), stackReference)) + if (areSlotsInSameInventory(slot, slotReference) == sameInventory && + areStacksEqual(slot.getStack(), stackReference)) + { + // Drop the stack + dropStack(gui, slot.id); + } + } + } + } + + public static void dropAllMatchingStacks(HandledScreen gui, + ItemStack stackReference) + { + if (isStackEmpty(stackReference) == false) + { + ScreenHandler container = gui.getScreenHandler(); + stackReference = stackReference.copy(); + + for (Slot slot : container.slots) + { + if (areStacksEqual(slot.getStack(), stackReference)) { // Drop the stack - dropStack(gui, slot.slotNumber); + dropStack(gui, slot.id); } } } } - public static boolean shiftDropItems(GuiContainer gui) + public static boolean shiftDropItems(HandledScreen gui) { - ItemStack stackReference = Minecraft.getMinecraft().player.inventory.getItemStack(); + ItemStack stackReference = gui.getScreenHandler().getCursorStack(); if (isStackEmpty(stackReference) == false && sourceSlot != null) { @@ -553,13 +639,13 @@ public static boolean shiftDropItems(GuiContainer gui) return false; } - public static boolean shiftPlaceItems(Slot slot, GuiContainer gui) + public static boolean shiftPlaceItems(Slot slot, HandledScreen gui) { // Left click to place the items from the cursor to the slot - leftClickSlot(gui, slot.slotNumber); + leftClickSlot(gui, slot.id); // Ugly fix to prevent accidentally drag-moving the stack from the slot that it was just placed into... - DRAGGED_SLOTS.add(slot.slotNumber); + DRAGGED_SLOTS.add(slot.id); tryMoveStacks(slot, gui, true, false, false); @@ -571,18 +657,18 @@ public static boolean shiftPlaceItems(Slot slot, GuiContainer gui) * The slot is then later used to determine which inventory an ItemStack was * picked up from, if the stack from the cursor is dropped while holding shift. */ - public static void storeSourceSlotCandidate(Slot slot, Minecraft mc) + public static void storeSourceSlotCandidate(Slot slot, HandledScreen gui) { // Left or right mouse button was pressed if (slot != null) { - ItemStack stackCursor = mc.player.inventory.getItemStack(); + ItemStack stackCursor = gui.getScreenHandler().getCursorStack(); ItemStack stack = EMPTY_STACK; if (isStackEmpty(stackCursor) == false) { // Do a cheap copy without NBT data - stack = new ItemStack(stackCursor.getItem(), getStackSize(stackCursor), stackCursor.getMetadata()); + stack = new ItemStack(stackCursor.getItem(), getStackSize(stackCursor)); } // Store the candidate @@ -596,19 +682,22 @@ public static void storeSourceSlotCandidate(Slot slot, Minecraft mc) /** * Check if the (previous) mouse event resulted in picking up a new ItemStack to the cursor */ - public static void checkForItemPickup(GuiContainer gui, Minecraft mc) + public static void checkForItemPickup(HandledScreen gui) { - ItemStack stackCursor = mc.player.inventory.getItemStack(); + ItemStack stackCursor = gui.getScreenHandler().getCursorStack(); // Picked up or swapped items to the cursor, grab a reference to the slot that the items came from - // Note that we are only checking the item and metadata here! + // Note that we are only checking the item here! if (isStackEmpty(stackCursor) == false && stackCursor.isItemEqual(stackInCursorLast) == false && sourceSlotCandidate != null) { sourceSlot = new WeakReference<>(sourceSlotCandidate.get()); } } - private static boolean tryMoveItemsVillager(GuiMerchant gui, Slot slot, boolean moveToOtherInventory, boolean fullStacks) + private static boolean tryMoveItemsVillager(MerchantScreen gui, + Slot slot, + boolean moveToOtherInventory, + boolean fullStacks) { if (fullStacks) { @@ -618,7 +707,7 @@ private static boolean tryMoveItemsVillager(GuiMerchant gui, Slot slot, boolean tryMoveItemsToMerchantBuySlots(gui, true); } // Move items from sell slot to player inventory - else if (slot.getHasStack()) + else if (slot.hasStack()) { tryMoveStacks(slot, gui, true, true, true); } @@ -636,7 +725,7 @@ else if (slot.getHasStack()) tryMoveItemsToMerchantBuySlots(gui, false); } // Scrolling items from this slot/inventory into the other inventory - else if (slot.getHasStack()) + else if (slot.hasStack()) { moveOneSetOfItemsFromSlotToPlayerInventory(gui, slot); } @@ -647,46 +736,48 @@ else if (slot.getHasStack()) public static void villagerClearTradeInputSlots() { - if (GuiUtils.getCurrentScreen() instanceof GuiMerchant) + if (GuiUtils.getCurrentScreen() instanceof MerchantScreen merchantGui) { - GuiMerchant merchantGui = (GuiMerchant) GuiUtils.getCurrentScreen(); - Slot slot = merchantGui.inventorySlots.getSlot(0); + Slot slot = merchantGui.getScreenHandler().getSlot(0); - if (slot.getHasStack()) + if (slot.hasStack()) { - shiftClickSlot(merchantGui, slot.slotNumber); + shiftClickSlot(merchantGui, slot.id); } - slot = merchantGui.inventorySlots.getSlot(1); + slot = merchantGui.getScreenHandler().getSlot(1); - if (slot.getHasStack()) + if (slot.hasStack()) { - InventoryUtils.shiftClickSlot(merchantGui, slot.slotNumber); + shiftClickSlot(merchantGui, slot.id); } } } - public static void villagerTradeEverythingPossibleWithCurrentRecipe() + public static void villagerTradeEverythingPossibleWithTrade(int visibleIndex) { - if (GuiUtils.getCurrentScreen() instanceof GuiMerchant) + if (GuiUtils.getCurrentScreen() instanceof MerchantScreen merchantGui) { - GuiMerchant merchantGui = (GuiMerchant) GuiUtils.getCurrentScreen(); - Slot slot = merchantGui.inventorySlots.getSlot(2); + MerchantScreenHandler handler = merchantGui.getScreenHandler(); + Slot slot = handler.getSlot(2); + ItemStack sellItem = handler.getRecipes().get(visibleIndex).getSellItem().copy(); while (true) { - InventoryUtils.tryMoveItemsToMerchantBuySlots(merchantGui, true); + VillagerUtils.switchToTradeByVisibleIndex(visibleIndex); + //tryMoveItemsToMerchantBuySlots(merchantGui, true); // Not a valid recipe - if (slot.getHasStack() == false) + //if (slot.hasStack() == false) + if (areStacksEqual(sellItem, slot.getStack()) == false) { break; } - InventoryUtils.shiftClickSlot(merchantGui, slot.slotNumber); + shiftClickSlot(merchantGui, slot.id); // No room in player inventory - if (slot.getHasStack()) + if (slot.hasStack()) { break; } @@ -696,46 +787,46 @@ public static void villagerTradeEverythingPossibleWithCurrentRecipe() } } - public static void villagerTradeEverythingPossibleWithAllFavoritedTrades() + public static boolean villagerTradeEverythingPossibleWithAllFavoritedTrades() { - if (GuiUtils.getCurrentScreen() instanceof GuiMerchant) - { - GuiMerchant merchantGui = (GuiMerchant) GuiUtils.getCurrentScreen(); - VillagerData data = VillagerDataStorage.getInstance().getDataForLastInteractionTarget(); + Screen screen = GuiUtils.getCurrentScreen(); - villagerClearTradeInputSlots(); + if (screen instanceof MerchantScreen) + { + MerchantScreenHandler handler = ((MerchantScreen) screen).getScreenHandler(); + IntArrayList favorites = VillagerDataStorage.getInstance().getFavoritesForCurrentVillager(handler).favorites; - if (data != null && data.getFavorites().isEmpty() == false) + for (int index = 0; index < favorites.size(); ++index) { - int initialPage = AccessorUtils.getSelectedMerchantRecipe(merchantGui); + VillagerUtils.switchToTradeByVisibleIndex(index); + villagerTradeEverythingPossibleWithTrade(index); + } - for (int index : data.getFavorites()) - { - InputHandler.changeTradePage(merchantGui, index); - villagerTradeEverythingPossibleWithCurrentRecipe(); - } + villagerClearTradeInputSlots(); - InputHandler.changeTradePage(merchantGui, initialPage); - } + return true; } + + return false; } - private static boolean tryMoveSingleItemToOtherInventory(Slot slot, GuiContainer gui) + private static boolean tryMoveSingleItemToOtherInventory(Slot slot, + HandledScreen gui) { ItemStack stackOrig = slot.getStack(); - Container container = gui.inventorySlots; - Minecraft mc = Minecraft.getMinecraft(); + ScreenHandler container = gui.getScreenHandler(); + MinecraftClient mc = MinecraftClient.getInstance(); - if (isStackEmpty(mc.player.inventory.getItemStack()) == false || slot.canTakeStack(mc.player) == false || - (getStackSize(stackOrig) > 1 && slot.isItemValid(stackOrig) == false)) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false || slot.canTakeItems(mc.player) == false || + (getStackSize(stackOrig) > 1 && slot.canInsert(stackOrig) == false)) { return false; } // Can take all the items to the cursor at once, use a shift-click method to move one item from the slot - if (getStackSize(stackOrig) <= stackOrig.getMaxStackSize()) + if (getStackSize(stackOrig) <= stackOrig.getMaxCount()) { - return clickSlotsToMoveSingleItemByShiftClick(gui, slot.slotNumber); + return clickSlotsToMoveSingleItemByShiftClick(gui, slot.id); } ItemStack stack = stackOrig.copy(); @@ -744,11 +835,11 @@ private static boolean tryMoveSingleItemToOtherInventory(Slot slot, GuiContainer ItemStack[] originalStacks = getOriginalStacks(container); // Try to move the temporary single-item stack via the shift-click handler method - slot.putStack(stack); - container.transferStackInSlot(mc.player, slot.slotNumber); + slot.setStackNoCallbacks(stack); + container.quickMove(mc.player, slot.id); // Successfully moved the item somewhere, now we want to check where it went - if (slot.getHasStack() == false) + if (slot.hasStack() == false) { int targetSlot = getTargetSlot(container, originalStacks); @@ -756,38 +847,39 @@ private static boolean tryMoveSingleItemToOtherInventory(Slot slot, GuiContainer if (targetSlot >= 0) { // Remove the dummy item from the target slot (on the client side) - container.inventorySlots.get(targetSlot).decrStackSize(1); + container.slots.get(targetSlot).takeStack(1); // Restore the original stack to the slot under the cursor (on the client side) restoreOriginalStacks(container, originalStacks); // Do the slot clicks to actually move the items (on the server side) - return clickSlotsToMoveSingleItem(gui, slot.slotNumber, targetSlot); + return clickSlotsToMoveSingleItem(gui, slot.id, targetSlot); } } // Restore the original stack to the slot under the cursor (on the client side) - slot.putStack(stackOrig); + slot.setStackNoCallbacks(stackOrig); return false; } - private static boolean tryMoveAllButOneItemToOtherInventory(Slot slot, GuiContainer gui) + private static boolean tryMoveAllButOneItemToOtherInventory(Slot slot, + HandledScreen gui) { - Minecraft mc = Minecraft.getMinecraft(); - EntityPlayer player = mc.player; + MinecraftClient mc = MinecraftClient.getInstance(); + PlayerEntity player = mc.player; ItemStack stackOrig = slot.getStack().copy(); - if (getStackSize(stackOrig) == 1 || getStackSize(stackOrig) > stackOrig.getMaxStackSize() || - slot.canTakeStack(player) == false || slot.isItemValid(stackOrig) == false) + if (getStackSize(stackOrig) == 1 || getStackSize(stackOrig) > stackOrig.getMaxCount() || + slot.canTakeItems(player) == false || slot.canInsert(stackOrig) == false) { return true; } // Take half of the items from the original slot to the cursor - rightClickSlot(gui, slot.slotNumber); + rightClickSlot(gui, slot.id); - ItemStack stackInCursor = player.inventory.getItemStack(); + ItemStack stackInCursor = gui.getScreenHandler().getCursorStack(); if (isStackEmpty(stackInCursor)) { return false; @@ -797,52 +889,52 @@ private static boolean tryMoveAllButOneItemToOtherInventory(Slot slot, GuiContai int tempSlotNum = -1; // Find some other slot where to store one of the items temporarily - for (Slot slotTmp : gui.inventorySlots.inventorySlots) + for (Slot slotTmp : gui.getScreenHandler().slots) { - if (slotTmp.slotNumber != slot.slotNumber && + if (slotTmp.id != slot.id && areSlotsInSameInventory(slotTmp, slot, true) && - slotTmp.isItemValid(stackInCursor) && - slotTmp.canTakeStack(player)) + slotTmp.canInsert(stackInCursor) && + slotTmp.canTakeItems(player)) { ItemStack stackInSlot = slotTmp.getStack(); if (isStackEmpty(stackInSlot) || areStacksEqual(stackInSlot, stackInCursor)) { // Try to put one item into the temporary slot - rightClickSlot(gui, slotTmp.slotNumber); + rightClickSlot(gui, slotTmp.id); - stackInCursor = player.inventory.getItemStack(); + stackInCursor = gui.getScreenHandler().getCursorStack(); // Successfully stored one item if (isStackEmpty(stackInCursor) || getStackSize(stackInCursor) < stackInCursorSizeOrig) { - tempSlotNum = slotTmp.slotNumber; + tempSlotNum = slotTmp.id; break; } } } } - if (isStackEmpty(player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { // Return the rest of the items into the original slot - leftClickSlot(gui, slot.slotNumber); + leftClickSlot(gui, slot.id); } // Successfully stored one item in a temporary slot if (tempSlotNum != -1) { // Shift click the stack from the original slot - shiftClickSlot(gui, slot.slotNumber); + shiftClickSlot(gui, slot.id); // Take half a stack from the temporary slot rightClickSlot(gui, tempSlotNum); // Return one item into the original slot - rightClickSlot(gui, slot.slotNumber); + rightClickSlot(gui, slot.id); // Return the rest of the items to the temporary slot, if any - if (isStackEmpty(player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { leftClickSlot(gui, tempSlotNum); } @@ -852,34 +944,34 @@ private static boolean tryMoveAllButOneItemToOtherInventory(Slot slot, GuiContai // No temporary slot found, try to move the stack manually else { - boolean treatHotbarAsDifferent = gui.getClass() == GuiInventory.class; - List slots = getSlotNumbersOfEmptySlots(gui.inventorySlots, slot, false, treatHotbarAsDifferent, false); + boolean treatHotbarAsDifferent = gui.getClass() == InventoryScreen.class; + IntArrayList slots = getSlotNumbersOfEmptySlots(gui.getScreenHandler(), slot, false, treatHotbarAsDifferent, false); if (slots.isEmpty()) { - slots = getSlotNumbersOfMatchingStacks(gui.inventorySlots, slot, false, slot.getStack(), true, treatHotbarAsDifferent, false); + slots = getSlotNumbersOfMatchingStacks(gui.getScreenHandler(), slot, false, slot.getStack(), true, treatHotbarAsDifferent, false); } if (slots.isEmpty() == false) { // Take the stack - leftClickSlot(gui, slot.slotNumber); + leftClickSlot(gui, slot.id); // Return one item - rightClickSlot(gui, slot.slotNumber); + rightClickSlot(gui, slot.id); // Try to place the stack in the cursor to any valid empty or matching slots in a different inventory for (int slotNum : slots) { - Slot slotTmp = gui.inventorySlots.getSlot(slotNum); - stackInCursor = player.inventory.getItemStack(); + Slot slotTmp = gui.getScreenHandler().getSlot(slotNum); + stackInCursor = gui.getScreenHandler().getCursorStack(); if (isStackEmpty(stackInCursor)) { return true; } - if (slotTmp.isItemValid(stackInCursor)) + if (slotTmp.canInsert(stackInCursor)) { leftClickSlot(gui, slotNum); } @@ -888,7 +980,7 @@ private static boolean tryMoveAllButOneItemToOtherInventory(Slot slot, GuiContai // Items left, return them if (isStackEmpty(stackInCursor) == false) { - leftClickSlot(gui, slot.slotNumber); + leftClickSlot(gui, slot.id); } } } @@ -896,29 +988,30 @@ private static boolean tryMoveAllButOneItemToOtherInventory(Slot slot, GuiContai return false; } - private static boolean tryMoveSingleItemToThisInventory(Slot slot, GuiContainer gui) + private static boolean tryMoveSingleItemToThisInventory(Slot slot, + HandledScreen gui) { - Container container = gui.inventorySlots; + ScreenHandler container = gui.getScreenHandler(); ItemStack stackOrig = slot.getStack(); - Minecraft mc = Minecraft.getMinecraft(); + MinecraftClient mc = MinecraftClient.getInstance(); - if (slot.isItemValid(stackOrig) == false) + if (slot.canInsert(stackOrig) == false) { return false; } - for (int slotNum = container.inventorySlots.size() - 1; slotNum >= 0; slotNum--) + for (int slotNum = container.slots.size() - 1; slotNum >= 0; slotNum--) { - Slot slotTmp = container.inventorySlots.get(slotNum); + Slot slotTmp = container.slots.get(slotNum); ItemStack stackTmp = slotTmp.getStack(); if (areSlotsInSameInventory(slotTmp, slot) == false && - isStackEmpty(stackTmp) == false && slotTmp.canTakeStack(mc.player) && - (getStackSize(stackTmp) == 1 || slotTmp.isItemValid(stackTmp))) + isStackEmpty(stackTmp) == false && slotTmp.canTakeItems(mc.player) && + (getStackSize(stackTmp) == 1 || slotTmp.canInsert(stackTmp))) { if (areStacksEqual(stackTmp, stackOrig)) { - return clickSlotsToMoveSingleItem(gui, slotTmp.slotNumber, slot.slotNumber); + return clickSlotsToMoveSingleItem(gui, slotTmp.id, slot.id); } } } @@ -926,17 +1019,17 @@ private static boolean tryMoveSingleItemToThisInventory(Slot slot, GuiContainer // If we weren't able to move any items from another inventory, then try to move items // within the same inventory (mostly between the hotbar and the player inventory) /* - for (Slot slotTmp : container.inventorySlots) + for (Slot slotTmp : container.slots) { ItemStack stackTmp = slotTmp.getStack(); - if (slotTmp.slotNumber != slot.slotNumber && - isStackEmpty(stackTmp) == false && slotTmp.canTakeStack(gui.mc.player) && - (getStackSize(stackTmp) == 1 || slotTmp.isItemValid(stackTmp))) + if (slotTmp.id != slot.id && + isStackEmpty(stackTmp) == false && slotTmp.canTakeItems(gui.mc.player) && + (getStackSize(stackTmp) == 1 || slotTmp.canInsert(stackTmp))) { if (areStacksEqual(stackTmp, stackOrig)) { - return this.clickSlotsToMoveSingleItem(gui, slotTmp.slotNumber, slot.slotNumber); + return this.clickSlotsToMoveSingleItem(gui, slotTmp.id, slot.id); } } } @@ -945,25 +1038,34 @@ private static boolean tryMoveSingleItemToThisInventory(Slot slot, GuiContainer return false; } - public static void tryMoveStacks(Slot slot, GuiContainer gui, boolean matchingOnly, boolean toOtherInventory, boolean firstOnly) + public static void tryMoveStacks(Slot slot, + HandledScreen gui, + boolean matchingOnly, + boolean toOtherInventory, + boolean firstOnly) { tryMoveStacks(slot.getStack(), slot, gui, matchingOnly, toOtherInventory, firstOnly); } - private static void tryMoveStacks(ItemStack stackReference, Slot slot, GuiContainer gui, boolean matchingOnly, boolean toOtherInventory, boolean firstOnly) + private static void tryMoveStacks(ItemStack stackReference, + Slot slot, + HandledScreen gui, + boolean matchingOnly, + boolean toOtherInventory, + boolean firstOnly) { - Container container = gui.inventorySlots; - final int maxSlot = container.inventorySlots.size() - 1; + ScreenHandler container = gui.getScreenHandler(); + final int maxSlot = container.slots.size() - 1; for (int i = maxSlot; i >= 0; i--) { - Slot slotTmp = container.inventorySlots.get(i); + Slot slotTmp = container.slots.get(i); - if (slotTmp.slotNumber != slot.slotNumber && - areSlotsInSameInventory(slotTmp, slot) == toOtherInventory && slotTmp.getHasStack() && + if (slotTmp.id != slot.id && + areSlotsInSameInventory(slotTmp, slot) == toOtherInventory && slotTmp.hasStack() && (matchingOnly == false || areStacksEqual(stackReference, slotTmp.getStack()))) { - boolean success = shiftClickSlotWithCheck(gui, slotTmp.slotNumber); + boolean success = shiftClickSlotWithCheck(gui, slotTmp.id); // Failed to shift-click items, try a manual method if (success == false && Configs.Toggles.SCROLL_STACKS_FALLBACK.getBooleanValue()) @@ -979,16 +1081,18 @@ private static void tryMoveStacks(ItemStack stackReference, Slot slot, GuiContai } // If moving to the other inventory, then move the hovered slot's stack last - if (toOtherInventory && shiftClickSlotWithCheck(gui, slot.slotNumber) == false) + if (toOtherInventory && + shiftClickSlotWithCheck(gui, slot.id) == false && + Configs.Toggles.SCROLL_STACKS_FALLBACK.getBooleanValue()) { clickSlotsToMoveItemsFromSlot(slot, gui, toOtherInventory); } } - public static void tryMoveItemsToMerchantBuySlots(GuiMerchant gui, boolean fillStacks) + private static void tryMoveItemsToMerchantBuySlots(MerchantScreen gui, + boolean fillStacks) { - Minecraft mc = Minecraft.getMinecraft(); - MerchantRecipeList list = gui.getMerchant().getRecipes(mc.player); + TradeOfferList list = gui.getScreenHandler().getRecipes(); int index = AccessorUtils.getSelectedMerchantRecipe(gui); if (list == null || list.size() <= index) @@ -996,14 +1100,15 @@ public static void tryMoveItemsToMerchantBuySlots(GuiMerchant gui, boolean fillS return; } - MerchantRecipe recipe = list.get(index); + TradeOffer recipe = list.get(index); + if (recipe == null) { return; } - ItemStack buy1 = recipe.getItemToBuy(); - ItemStack buy2 = recipe.getSecondItemToBuy(); + ItemStack buy1 = recipe.getAdjustedFirstBuyItem(); + ItemStack buy2 = recipe.getSecondBuyItem(); if (isStackEmpty(buy1) == false) { @@ -1016,11 +1121,14 @@ public static void tryMoveItemsToMerchantBuySlots(GuiMerchant gui, boolean fillS } } - private static void fillBuySlot(GuiContainer gui, int slotNum, ItemStack buyStack, boolean fillStacks) + private static void fillBuySlot(HandledScreen gui, + int slotNum, + ItemStack buyStack, + boolean fillStacks) { - Slot slot = gui.inventorySlots.getSlot(slotNum); + Slot slot = gui.getScreenHandler().getSlot(slotNum); ItemStack existingStack = slot.getStack(); - Minecraft mc = Minecraft.getMinecraft(); + MinecraftClient mc = MinecraftClient.getInstance(); // If there are items not matching the merchant recipe, move them out first if (isStackEmpty(existingStack) == false && areStacksEqual(buyStack, existingStack) == false) @@ -1032,12 +1140,18 @@ private static void fillBuySlot(GuiContainer gui, int slotNum, ItemStack buyStac if (isStackEmpty(existingStack) || areStacksEqual(buyStack, existingStack)) { - moveItemsFromInventory(gui, slotNum, mc.player.inventory, buyStack, fillStacks); + moveItemsFromInventory(gui, slotNum, mc.player.getInventory(), buyStack, fillStacks); } } - public static void handleRecipeClick(GuiContainer gui, Minecraft mc, RecipeStorage recipes, int hoveredRecipeId, - boolean isLeftClick, boolean isRightClick, boolean isPickBlock, boolean isShiftDown) + public static void handleRecipeClick(HandledScreen gui, + MinecraftClient mc, + RecipeStorage recipes, + int hoveredRecipeId, + boolean isLeftClick, + boolean isRightClick, + boolean isPickBlock, + boolean isShiftDown) { if (isLeftClick || isRightClick) { @@ -1057,7 +1171,7 @@ public static void handleRecipeClick(GuiContainer gui, Minecraft mc, RecipeStora if (isRightClick) { Slot outputSlot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); - boolean dropKeyDown = mc.gameSettings.keyBindDrop.isKeyDown(); + boolean dropKeyDown = mc.options.dropKey.isPressed(); // FIXME 1.14 if (outputSlot != null) { @@ -1067,23 +1181,23 @@ public static void handleRecipeClick(GuiContainer gui, Minecraft mc, RecipeStora { if (Configs.Generic.CARPET_CTRL_Q_CRAFTING.getBooleanValue()) { - InventoryUtils.dropStack(gui, outputSlot.slotNumber); + InventoryUtils.dropStack(gui, outputSlot.id); } else { - InventoryUtils.dropStacksUntilEmpty(gui, outputSlot.slotNumber); + InventoryUtils.dropStacksUntilEmpty(gui, outputSlot.id); } } else { - InventoryUtils.dropItem(gui, outputSlot.slotNumber); + InventoryUtils.dropItem(gui, outputSlot.id); } } else { if (isShiftDown) { - InventoryUtils.shiftClickSlot(gui, outputSlot.slotNumber); + InventoryUtils.shiftClickSlot(gui, outputSlot.id); } else { @@ -1099,7 +1213,9 @@ else if (isPickBlock) } } - public static void tryMoveItemsToFirstCraftingGrid(CraftingRecipe recipe, GuiContainer gui, boolean fillStacks) + public static void tryMoveItemsToFirstCraftingGrid(RecipePattern recipe, + HandledScreen gui, + boolean fillStacks) { Slot craftingOutputSlot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); @@ -1109,31 +1225,38 @@ public static void tryMoveItemsToFirstCraftingGrid(CraftingRecipe recipe, GuiCon } } - public static void loadRecipeItemsToGridForOutputSlotUnderMouse(CraftingRecipe recipe, GuiContainer gui) + public static void loadRecipeItemsToGridForOutputSlotUnderMouse(RecipePattern recipe, + HandledScreen gui) { Slot slot = AccessorUtils.getSlotUnderMouse(gui); loadRecipeItemsToGridForOutputSlot(recipe, gui, slot); } - private static void loadRecipeItemsToGridForOutputSlot(CraftingRecipe recipe, GuiContainer gui, Slot outputSlot) + private static void loadRecipeItemsToGridForOutputSlot(RecipePattern recipe, + HandledScreen gui, + Slot outputSlot) { - if (outputSlot != null && isCraftingSlot(gui, outputSlot) && isStackEmpty(recipe.getResult()) == false) + if (isCraftingSlot(gui, outputSlot) && isStackEmpty(recipe.getResult()) == false) { tryMoveItemsToCraftingGridSlots(recipe, outputSlot, gui, false); } } - private static boolean tryMoveItemsCrafting(RecipeStorage recipes, Slot slot, GuiContainer gui, - boolean moveToOtherInventory, boolean moveStacks, boolean moveEverything) + private static boolean tryMoveItemsCrafting(RecipeStorage recipes, + Slot slot, + HandledScreen gui, + boolean moveToOtherInventory, + boolean moveStacks, + boolean moveEverything) { - CraftingRecipe recipe = recipes.getSelectedRecipe(); + RecipePattern recipe = recipes.getSelectedRecipe(); ItemStack stackRecipeOutput = recipe.getResult(); // Try to craft items if (moveToOtherInventory) { // Items in the output slot - if (slot.getHasStack()) + if (slot.hasStack()) { // The output item matches the current recipe if (areStacksEqual(slot.getStack(), stackRecipeOutput)) @@ -1144,7 +1267,7 @@ private static boolean tryMoveItemsCrafting(RecipeStorage recipes, Slot slot, Gu } else if (moveStacks) { - shiftClickSlot(gui, slot.slotNumber); + shiftClickSlot(gui, slot.id); } else { @@ -1167,17 +1290,19 @@ else if (moveToOtherInventory == false && isStackEmpty(stackRecipeOutput) == fal return false; } - private static void craftAsManyItemsAsPossible(CraftingRecipe recipe, Slot slot, GuiContainer gui) + private static void craftAsManyItemsAsPossible(RecipePattern recipe, + Slot slot, + HandledScreen gui) { ItemStack result = recipe.getResult(); int failSafe = 1024; - while (failSafe > 0 && slot.getHasStack() && areStacksEqual(slot.getStack(), result)) + while (failSafe > 0 && slot.hasStack() && areStacksEqual(slot.getStack(), result)) { - shiftClickSlot(gui, slot.slotNumber); + shiftClickSlot(gui, slot.id); // Ran out of some or all ingredients for the recipe - if (slot.getHasStack() == false || areStacksEqual(slot.getStack(), result) == false) + if (slot.hasStack() == false || areStacksEqual(slot.getStack(), result) == false) { tryMoveItemsToCraftingGridSlots(recipe, slot, gui, true); } @@ -1191,7 +1316,9 @@ private static void craftAsManyItemsAsPossible(CraftingRecipe recipe, Slot slot, } } - public static void clearFirstCraftingGridOfItems(CraftingRecipe recipe, GuiContainer gui, boolean clearNonMatchingOnly) + public static void clearFirstCraftingGridOfItems(RecipePattern recipe, + HandledScreen gui, + boolean clearNonMatchingOnly) { Slot craftingOutputSlot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); @@ -1202,7 +1329,7 @@ public static void clearFirstCraftingGridOfItems(CraftingRecipe recipe, GuiConta } } - public static void clearFirstCraftingGridOfAllItems(GuiContainer gui) + public static void clearFirstCraftingGridOfAllItems(HandledScreen gui) { Slot craftingOutputSlot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); @@ -1213,26 +1340,29 @@ public static void clearFirstCraftingGridOfAllItems(GuiContainer gui) } } - private static boolean clearCraftingGridOfItems(CraftingRecipe recipe, GuiContainer gui, SlotRange range, boolean clearNonMatchingOnly) + private static boolean clearCraftingGridOfItems(RecipePattern recipe, + HandledScreen gui, + SlotRange range, + boolean clearNonMatchingOnly) { - final int invSlots = gui.inventorySlots.inventorySlots.size(); + final int invSlots = gui.getScreenHandler().slots.size(); final int rangeSlots = range.getSlotCount(); final int recipeSize = recipe.getRecipeLength(); final int slotCount = Math.min(rangeSlots, recipeSize); for (int i = 0, slotNum = range.getFirst(); i < slotCount && slotNum < invSlots; i++, slotNum++) { - Slot slotTmp = gui.inventorySlots.getSlot(slotNum); + Slot slotTmp = gui.getScreenHandler().getSlot(slotNum); - if (slotTmp != null && slotTmp.getHasStack() && + if (slotTmp != null && slotTmp.hasStack() && (clearNonMatchingOnly == false || areStacksEqual(recipe.getRecipeItems()[i], slotTmp.getStack()) == false)) { shiftClickSlot(gui, slotNum); // Failed to clear the slot - if (slotTmp.getHasStack()) + if (slotTmp.hasStack()) { - return false; + dropStack(gui, slotNum); } } } @@ -1240,22 +1370,22 @@ private static boolean clearCraftingGridOfItems(CraftingRecipe recipe, GuiContai return true; } - private static boolean clearCraftingGridOfAllItems(GuiContainer gui, SlotRange range) + private static boolean clearCraftingGridOfAllItems(HandledScreen gui, SlotRange range) { - final int invSlots = gui.inventorySlots.inventorySlots.size(); + final int invSlots = gui.getScreenHandler().slots.size(); final int rangeSlots = range.getSlotCount(); boolean clearedAll = true; for (int i = 0, slotNum = range.getFirst(); i < rangeSlots && slotNum < invSlots; i++, slotNum++) { - Slot slotTmp = gui.inventorySlots.getSlot(slotNum); + Slot slotTmp = gui.getScreenHandler().getSlot(slotNum); - if (slotTmp != null && slotTmp.getHasStack()) + if (slotTmp != null && slotTmp.hasStack()) { shiftClickSlot(gui, slotNum); // Failed to clear the slot - if (slotTmp.getHasStack()) + if (slotTmp.hasStack()) { clearedAll = false; } @@ -1265,10 +1395,13 @@ private static boolean clearCraftingGridOfAllItems(GuiContainer gui, SlotRange r return clearedAll; } - private static boolean tryMoveItemsToCraftingGridSlots(CraftingRecipe recipe, Slot slot, GuiContainer gui, boolean fillStacks) + private static boolean tryMoveItemsToCraftingGridSlots(RecipePattern recipe, + Slot slot, + HandledScreen gui, + boolean fillStacks) { - Container container = gui.inventorySlots; - int numSlots = container.inventorySlots.size(); + ScreenHandler container = gui.getScreenHandler(); + int numSlots = container.slots.size(); SlotRange range = CraftingHandler.getCraftingGridSlots(gui, slot); // Check that the slot range is valid and that the recipe can fit into this type of crafting grid @@ -1282,13 +1415,13 @@ private static boolean tryMoveItemsToCraftingGridSlots(CraftingRecipe recipe, Sl // This slot is used to check that we get items from a DIFFERENT inventory than where this slot is in Slot slotGridFirst = container.getSlot(range.getFirst()); - Map> ingredientSlots = ItemType.getSlotsPerItem(recipe.getRecipeItems()); + Map ingredientSlots = ItemType.getSlotsPerItem(recipe.getRecipeItems()); - for (Map.Entry> entry : ingredientSlots.entrySet()) + for (Map.Entry entry : ingredientSlots.entrySet()) { ItemStack ingredientReference = entry.getKey().getStack(); - List recipeSlots = entry.getValue(); - List targetSlots = new ArrayList(); + IntArrayList recipeSlots = entry.getValue(); + IntArrayList targetSlots = new IntArrayList(); // Get the actual target slot numbers based on the grid's start and the relative positions inside the grid for (int s : recipeSlots) @@ -1310,14 +1443,15 @@ private static boolean tryMoveItemsToCraftingGridSlots(CraftingRecipe recipe, Sl return false; } - private static void fillCraftingGrid(GuiContainer gui, Slot slotGridFirst, ItemStack ingredientReference, List targetSlots) + private static void fillCraftingGrid(HandledScreen gui, + Slot slotGridFirst, + ItemStack ingredientReference, + IntArrayList targetSlots) { - Container container = gui.inventorySlots; - Minecraft mc = Minecraft.getMinecraft(); - EntityPlayer player = mc.player; - int slotNum = -1; + ScreenHandler container = gui.getScreenHandler(); + int slotNum; int slotReturn = -1; - int sizeOrig = 0; + int sizeOrig; if (isStackEmpty(ingredientReference)) { @@ -1342,14 +1476,14 @@ private static void fillCraftingGrid(GuiContainer gui, Slot slotGridFirst, ItemS // Pick up the ingredient stack from the found slot leftClickSlot(gui, slotNum); - ItemStack stackCursor = player.inventory.getItemStack(); + ItemStack stackCursor = gui.getScreenHandler().getCursorStack(); // Successfully picked up ingredient items if (areStacksEqual(ingredientReference, stackCursor)) { sizeOrig = getStackSize(stackCursor); dragSplitItemsIntoSlots(gui, targetSlots); - stackCursor = player.inventory.getItemStack(); + stackCursor = gui.getScreenHandler().getCursorStack(); // Items left in cursor if (isStackEmpty(stackCursor) == false) @@ -1364,7 +1498,7 @@ private static void fillCraftingGrid(GuiContainer gui, Slot slotGridFirst, ItemS leftClickSlot(gui, slotReturn); // All of them didn't fit into the first slot anymore, switch into the current source slot - if (isStackEmpty(player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { slotReturn = slotNum; leftClickSlot(gui, slotReturn); @@ -1379,27 +1513,25 @@ private static void fillCraftingGrid(GuiContainer gui, Slot slotGridFirst, ItemS } // Somehow items were left in the cursor, break here - if (isStackEmpty(player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { break; } } // Return the rest of the items to the original slot - if (slotNum >= 0 && isStackEmpty(player.inventory.getItemStack()) == false) + if (slotNum >= 0 && isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { leftClickSlot(gui, slotNum); } } - public static void rightClickCraftOneStack(GuiContainer gui) + public static void rightClickCraftOneStack(HandledScreen gui) { Slot slot = AccessorUtils.getSlotUnderMouse(gui); - Minecraft mc = Minecraft.getMinecraft(); - InventoryPlayer inv = mc.player.inventory; - ItemStack stackCursor = inv.getItemStack(); + ItemStack stackCursor = gui.getScreenHandler().getCursorStack(); - if (slot == null || slot.getHasStack() == false || + if (slot == null || slot.hasStack() == false || (isStackEmpty(stackCursor) == false) && areStacksEqual(slot.getStack(), stackCursor) == false) { return; @@ -1409,12 +1541,12 @@ public static void rightClickCraftOneStack(GuiContainer gui) while (true) { - rightClickSlot(gui, slot.slotNumber); - stackCursor = inv.getItemStack(); + rightClickSlot(gui, slot.id); + stackCursor = gui.getScreenHandler().getCursorStack(); // Failed to craft items, or the stack became full, or ran out of ingredients if (isStackEmpty(stackCursor) || getStackSize(stackCursor) <= sizeLast || - getStackSize(stackCursor) >= stackCursor.getMaxStackSize() || + getStackSize(stackCursor) >= stackCursor.getMaxCount() || areStacksEqual(slot.getStack(), stackCursor) == false) { break; @@ -1424,7 +1556,8 @@ public static void rightClickCraftOneStack(GuiContainer gui) } } - public static void craftEverythingPossibleWithCurrentRecipe(CraftingRecipe recipe, GuiContainer gui) + public static void craftEverythingPossibleWithCurrentRecipe(RecipePattern recipe, + HandledScreen gui) { Slot slot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); @@ -1442,7 +1575,7 @@ public static void craftEverythingPossibleWithCurrentRecipe(CraftingRecipe recip tryMoveItemsToCraftingGridSlots(recipe, slot, gui, true); - if (slot.getHasStack()) + if (slot.hasStack()) { craftAsManyItemsAsPossible(recipe, slot, gui); } @@ -1450,18 +1583,19 @@ public static void craftEverythingPossibleWithCurrentRecipe(CraftingRecipe recip } } - public static void moveAllCraftingResultsToOtherInventory(CraftingRecipe recipe, GuiContainer gui) + public static void moveAllCraftingResultsToOtherInventory(RecipePattern recipe, + HandledScreen gui) { if (isStackEmpty(recipe.getResult()) == false) { Slot slot = null; ItemStack stackResult = recipe.getResult().copy(); - for (Slot slotTmp : gui.inventorySlots.inventorySlots) + for (Slot slotTmp : gui.getScreenHandler().slots) { // This slot is likely in the player inventory, as there is another inventory above if (areStacksEqual(slotTmp.getStack(), stackResult) && - inventoryExistsAbove(slotTmp, gui.inventorySlots)) + inventoryExistsAbove(slotTmp, gui.getScreenHandler())) { slot = slotTmp; break; @@ -1472,7 +1606,7 @@ public static void moveAllCraftingResultsToOtherInventory(CraftingRecipe recipe, { // Get a list of slots with matching items, which are in the same inventory // as the slot that is assumed to be in the player inventory. - List slots = getSlotNumbersOfMatchingStacks(gui.inventorySlots, slot, true, stackResult, false, false, false); + IntArrayList slots = getSlotNumbersOfMatchingStacks(gui.getScreenHandler(), slot, true, stackResult, false, false, false); for (int slotNum : slots) { @@ -1482,7 +1616,8 @@ public static void moveAllCraftingResultsToOtherInventory(CraftingRecipe recipe, } } - public static void throwAllCraftingResultsToGround(CraftingRecipe recipe, GuiContainer gui) + public static void throwAllCraftingResultsToGround(RecipePattern recipe, + HandledScreen gui) { Slot slot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); @@ -1492,24 +1627,114 @@ public static void throwAllCraftingResultsToGround(CraftingRecipe recipe, GuiCon } } - private static int putSingleItemIntoSlots(GuiContainer gui, List targetSlots, int startIndex) + public static void throwAllNonRecipeItemsToGround(RecipePattern recipe, + HandledScreen gui) + { + Slot outputSlot = CraftingHandler.getFirstCraftingOutputSlotForGui(gui); + + if (outputSlot != null && isStackEmpty(recipe.getResult()) == false) + { + SlotRange range = CraftingHandler.getCraftingGridSlots(gui, outputSlot); + ItemStack[] recipeItems = recipe.getRecipeItems(); + final int invSlots = gui.getScreenHandler().slots.size(); + final int rangeSlots = Math.min(range.getSlotCount(), recipeItems.length); + + for (int i = 0, slotNum = range.getFirst(); i < rangeSlots && slotNum < invSlots; i++, slotNum++) + { + Slot slotTmp = gui.getScreenHandler().getSlot(slotNum); + ItemStack stack = slotTmp.getStack(); + + if (stack.isEmpty() == false && areStacksEqual(stack, recipeItems[i]) == false) + { + dropAllMatchingStacks(gui, stack); + } + } + } + } + + public static void setCraftingGridContentsUsingSwaps(HandledScreen gui, + PlayerInventory inv, + RecipePattern recipe, + Slot outputSlot) { - Minecraft mc = Minecraft.getMinecraft(); - ItemStack stackInCursor = mc.player.inventory.getItemStack(); + SlotRange range = CraftingHandler.getCraftingGridSlots(gui, outputSlot); + + if (range != null && isStackEmpty(recipe.getResult()) == false) + { + ItemStack[] recipeItems = recipe.getRecipeItems(); + final int invSlots = gui.getScreenHandler().slots.size(); + final int rangeSlots = Math.min(range.getSlotCount(), recipeItems.length); + IntArrayList toRemove = new IntArrayList(); + boolean movedSomething = false; + + setInhibitCraftingOutputUpdate(true); + + for (int i = 0, slotNum = range.getFirst(); i < rangeSlots && slotNum < invSlots; i++, slotNum++) + { + Slot slotTmp = gui.getScreenHandler().getSlot(slotNum); + ItemStack recipeStack = recipeItems[i]; + ItemStack slotStack = slotTmp.getStack(); + boolean recipeHasItem = isStackEmpty(recipeStack) == false; + + if (areStacksEqual(recipeStack, slotStack) == false) + { + if (recipeHasItem == false) + { + toRemove.add(slotNum); + } + else + { + int index = getPlayerInventoryIndexWithItem(recipeStack, inv); + + if (index >= 0) + { + clickSlot(gui, slotNum, index, SlotActionType.SWAP); + movedSomething = true; + } + } + } + } + + movedSomething |= (toRemove.isEmpty() == false); + + for (int slotNum : toRemove) + { + shiftClickSlot(gui, slotNum); + + if (isStackEmpty(gui.getScreenHandler().getSlot(slotNum).getStack()) == false) + { + dropStack(gui, slotNum); + } + } + + setInhibitCraftingOutputUpdate(false); + + if (movedSomething) + { + updateCraftingOutputSlot(outputSlot); + } + } + } + + private static int putSingleItemIntoSlots(HandledScreen gui, + IntArrayList targetSlots, + int startIndex) + { + ItemStack stackInCursor = gui.getScreenHandler().getCursorStack(); if (isStackEmpty(stackInCursor)) { return 0; } - int numSlots = gui.inventorySlots.inventorySlots.size(); + int numSlots = gui.getScreenHandler().slots.size(); int numItems = getStackSize(stackInCursor); int loops = Math.min(numItems, targetSlots.size() - startIndex); int count = 0; for (int i = 0; i < loops; i++) { - int slotNum = targetSlots.get(startIndex + i); + int slotNum = targetSlots.getInt(startIndex + i); if (slotNum >= numSlots) { @@ -1523,29 +1748,31 @@ private static int putSingleItemIntoSlots(GuiContainer gui, List target return count; } - public static void moveOneSetOfItemsFromSlotToPlayerInventory(GuiContainer gui, Slot slot) + public static void moveOneSetOfItemsFromSlotToPlayerInventory(HandledScreen gui, + Slot slot) { - leftClickSlot(gui, slot.slotNumber); + leftClickSlot(gui, slot.id); - Minecraft mc = Minecraft.getMinecraft(); - ItemStack stackCursor = mc.player.inventory.getItemStack(); + ItemStack stackCursor = gui.getScreenHandler().getCursorStack(); if (isStackEmpty(stackCursor) == false) { - List slots = getSlotNumbersOfMatchingStacks(gui.inventorySlots, slot, false, stackCursor, true, true, false); + IntArrayList slots = getSlotNumbersOfMatchingStacks(gui.getScreenHandler(), slot, false, stackCursor, true, true, false); if (moveItemFromCursorToSlots(gui, slots) == false) { - slots = getSlotNumbersOfEmptySlotsInPlayerInventory(gui.inventorySlots, false); + slots = getSlotNumbersOfEmptySlotsInPlayerInventory(gui.getScreenHandler(), false); moveItemFromCursorToSlots(gui, slots); } } } - private static void moveOneRecipeItemIntoCraftingGrid(GuiContainer gui, Slot slotGridFirst, ItemStack ingredientReference, List targetSlots) + private static void moveOneRecipeItemIntoCraftingGrid(HandledScreen gui, + Slot slotGridFirst, + ItemStack ingredientReference, + IntArrayList targetSlots) { - Container container = gui.inventorySlots; - Minecraft mc = Minecraft.getMinecraft(); + ScreenHandler container = gui.getScreenHandler(); int index = 0; int slotNum = -1; int slotCount = targetSlots.size(); @@ -1564,7 +1791,7 @@ private static void moveOneRecipeItemIntoCraftingGrid(GuiContainer gui, Slot slo leftClickSlot(gui, slotNum); // Successfully picked up ingredient items - if (areStacksEqual(ingredientReference, mc.player.inventory.getItemStack())) + if (areStacksEqual(ingredientReference, gui.getScreenHandler().getCursorStack())) { int filled = putSingleItemIntoSlots(gui, targetSlots, index); index += filled; @@ -1583,22 +1810,20 @@ private static void moveOneRecipeItemIntoCraftingGrid(GuiContainer gui, Slot slo } // Return the rest of the items to the original slot - if (slotNum >= 0 && isStackEmpty(mc.player.inventory.getItemStack()) == false) + if (slotNum >= 0 && isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { leftClickSlot(gui, slotNum); } } - private static boolean moveItemFromCursorToSlots(GuiContainer gui, List slotNumbers) + private static boolean moveItemFromCursorToSlots(HandledScreen gui, + IntArrayList slotNumbers) { - Minecraft mc = Minecraft.getMinecraft(); - InventoryPlayer inv = mc.player.inventory; - for (int slotNum : slotNumbers) { leftClickSlot(gui, slotNum); - if (isStackEmpty(inv.getItemStack())) + if (isStackEmpty(gui.getScreenHandler().getCursorStack())) { return true; } @@ -1607,11 +1832,15 @@ private static boolean moveItemFromCursorToSlots(GuiContainer gui, List return false; } - private static void moveItemsFromInventory(GuiContainer gui, int slotTo, IInventory invSrc, ItemStack stackTemplate, boolean fillStacks) + private static void moveItemsFromInventory(HandledScreen gui, + int slotTo, + Inventory invSrc, + ItemStack stackTemplate, + boolean fillStacks) { - Container container = gui.inventorySlots; + ScreenHandler container = gui.getScreenHandler(); - for (Slot slot : container.inventorySlots) + for (Slot slot : container.slots) { if (slot == null) { @@ -1622,35 +1851,37 @@ private static void moveItemsFromInventory(GuiContainer gui, int slotTo, IInvent { if (fillStacks) { - if (clickSlotsToMoveItems(gui, slot.slotNumber, slotTo) == false) + if (clickSlotsToMoveItems(gui, slot.id, slotTo) == false) { break; } } else { - clickSlotsToMoveSingleItem(gui, slot.slotNumber, slotTo); + clickSlotsToMoveSingleItem(gui, slot.id, slotTo); break; } } } } - private static int getSlotNumberOfLargestMatchingStackFromDifferentInventory(Container container, Slot slotReference, ItemStack stackReference) + private static int getSlotNumberOfLargestMatchingStackFromDifferentInventory(ScreenHandler container, + Slot slotReference, + ItemStack stackReference) { int slotNum = -1; int largest = 0; - for (Slot slot : container.inventorySlots) + for (Slot slot : container.slots) { - if (areSlotsInSameInventory(slot, slotReference) == false && slot.getHasStack() && + if (areSlotsInSameInventory(slot, slotReference) == false && slot.hasStack() && areStacksEqual(stackReference, slot.getStack())) { int stackSize = getStackSize(slot.getStack()); if (stackSize > largest) { - slotNum = slot.slotNumber; + slotNum = slot.id; largest = stackSize; } } @@ -1663,53 +1894,39 @@ private static int getSlotNumberOfLargestMatchingStackFromDifferentInventory(Con * Returns the slot number of the slot that has the smallest stackSize that is still equal to or larger * than idealSize. The slot must also NOT be in the same inventory as slotReference. * If an adequately large stack is not found, then the largest one is selected. - * @param container - * @param slotReference - * @param stackReference - * @return */ - private static int getSlotNumberOfSmallestStackFromDifferentInventory(Container container, Slot slotReference, ItemStack stackReference, int idealSize) + private static int getSlotNumberOfSmallestStackFromDifferentInventory(ScreenHandler container, + Slot slotReference, + ItemStack stackReference, + int idealSize) { - int slotNum = -1; + int slotNumSmallest = -1; + int slotNumLargest = -1; int smallest = Integer.MAX_VALUE; + int largest = 0; - for (Slot slot : container.inventorySlots) + for (Slot slot : container.slots) { - if (areSlotsInSameInventory(slot, slotReference) == false && slot.getHasStack() && + if (areSlotsInSameInventory(slot, slotReference) == false && slot.hasStack() && areStacksEqual(stackReference, slot.getStack())) { int stackSize = getStackSize(slot.getStack()); if (stackSize < smallest && stackSize >= idealSize) { - slotNum = slot.slotNumber; + slotNumSmallest = slot.id; smallest = stackSize; } - } - } - // Didn't find an adequately sized stack, now try to find at least some items... - if (slotNum == -1) - { - int largest = 0; - - for (Slot slot : container.inventorySlots) - { - if (areSlotsInSameInventory(slot, slotReference) == false && slot.getHasStack() && - areStacksEqual(stackReference, slot.getStack())) + if (stackSize > largest) { - int stackSize = getStackSize(slot.getStack()); - - if (stackSize > largest) - { - slotNum = slot.slotNumber; - largest = stackSize; - } + slotNumLargest = slot.id; + largest = stackSize; } } } - return slotNum; + return slotNumSmallest != -1 ? slotNumSmallest : slotNumLargest; } /** @@ -1725,29 +1942,34 @@ private static int getSlotNumberOfSmallestStackFromDifferentInventory(Container * @param reverse if true, returns the slots starting from the end of the inventory * @return */ - private static List getSlotNumbersOfMatchingStacks( - Container container, Slot slotReference, boolean sameInventory, - ItemStack stackReference, boolean preferPartial, boolean treatHotbarAsDifferent, boolean reverse) - { - List slots = new ArrayList(64); - final int maxSlot = container.inventorySlots.size() - 1; + @SuppressWarnings("SameParameterValue") + private static IntArrayList getSlotNumbersOfMatchingStacks(ScreenHandler container, + Slot slotReference, + boolean sameInventory, + ItemStack stackReference, + boolean preferPartial, + boolean treatHotbarAsDifferent, + boolean reverse) + { + IntArrayList slots = new IntArrayList(64); + final int maxSlot = container.slots.size() - 1; final int increment = reverse ? -1 : 1; for (int i = reverse ? maxSlot : 0; i >= 0 && i <= maxSlot; i += increment) { Slot slot = container.getSlot(i); - if (slot != null && slot.getHasStack() && + if (slot != null && slot.hasStack() && areSlotsInSameInventory(slot, slotReference, treatHotbarAsDifferent) == sameInventory && areStacksEqual(slot.getStack(), stackReference)) { - if ((getStackSize(slot.getStack()) < stackReference.getMaxStackSize()) == preferPartial) + if ((getStackSize(slot.getStack()) < stackReference.getMaxCount()) == preferPartial) { - slots.add(0, slot.slotNumber); + slots.add(0, slot.id); } else { - slots.add(slot.slotNumber); + slots.add(slot.id); } } } @@ -1755,24 +1977,27 @@ private static List getSlotNumbersOfMatchingStacks( return slots; } - private static List getSlotNumbersOfMatchingStacks(Container container, ItemStack stackReference, boolean preferPartial) + @SuppressWarnings("SameParameterValue") + private static IntArrayList getSlotNumbersOfMatchingStacks(ScreenHandler container, + ItemStack stackReference, + boolean preferPartial) { - List slots = new ArrayList(64); - final int maxSlot = container.inventorySlots.size() - 1; + IntArrayList slots = new IntArrayList(64); + final int maxSlot = container.slots.size() - 1; for (int i = 0; i <= maxSlot; ++i) { Slot slot = container.getSlot(i); - if (slot != null && slot.getHasStack() && areStacksEqual(slot.getStack(), stackReference)) + if (slot != null && slot.hasStack() && areStacksEqual(slot.getStack(), stackReference)) { - if ((getStackSize(slot.getStack()) < stackReference.getMaxStackSize()) == preferPartial) + if ((getStackSize(slot.getStack()) < stackReference.getMaxCount()) == preferPartial) { - slots.add(0, slot.slotNumber); + slots.add(0, slot.id); } else { - slots.add(slot.slotNumber); + slots.add(slot.id); } } } @@ -1780,40 +2005,63 @@ private static List getSlotNumbersOfMatchingStacks(Container container, return slots; } - private static List getSlotNumbersOfEmptySlots( - Container container, Slot slotReference, boolean sameInventory, boolean treatHotbarAsDifferent, boolean reverse) + public static int getPlayerInventoryIndexWithItem(ItemStack stackReference, PlayerInventory inv) { - List slots = new ArrayList(64); - final int maxSlot = container.inventorySlots.size() - 1; + final int size = inv.main.size(); + + for (int index = 0; index < size; ++index) + { + ItemStack stack = inv.main.get(index); + + if (areStacksEqual(stack, stackReference)) + { + return index; + } + } + + return -1; + } + + @SuppressWarnings("SameParameterValue") + private static IntArrayList getSlotNumbersOfEmptySlots(ScreenHandler container, + Slot slotReference, + boolean sameInventory, + boolean treatHotbarAsDifferent, + boolean reverse) + { + IntArrayList slots = new IntArrayList(64); + final int maxSlot = container.slots.size() - 1; final int increment = reverse ? -1 : 1; for (int i = reverse ? maxSlot : 0; i >= 0 && i <= maxSlot; i += increment) { Slot slot = container.getSlot(i); - if (slot != null && slot.getHasStack() == false && + if (slot != null && slot.hasStack() == false && areSlotsInSameInventory(slot, slotReference, treatHotbarAsDifferent) == sameInventory) { - slots.add(slot.slotNumber); + slots.add(slot.id); } } return slots; } - private static List getSlotNumbersOfEmptySlotsInPlayerInventory(Container container, boolean reverse) + @SuppressWarnings("SameParameterValue") + private static IntArrayList getSlotNumbersOfEmptySlotsInPlayerInventory(ScreenHandler container, + boolean reverse) { - List slots = new ArrayList(64); - final int maxSlot = container.inventorySlots.size() - 1; + IntArrayList slots = new IntArrayList(64); + final int maxSlot = container.slots.size() - 1; final int increment = reverse ? -1 : 1; for (int i = reverse ? maxSlot : 0; i >= 0 && i <= maxSlot; i += increment) { Slot slot = container.getSlot(i); - if (slot != null && (slot.inventory instanceof InventoryPlayer) && slot.getHasStack() == false) + if (slot != null && (slot.inventory instanceof PlayerInventory) && slot.hasStack() == false) { - slots.add(slot.slotNumber); + slots.add(slot.id); } } @@ -1822,7 +2070,7 @@ private static List getSlotNumbersOfEmptySlotsInPlayerInventory(Contain public static boolean areStacksEqual(ItemStack stack1, ItemStack stack2) { - return ItemStack.areItemsEqual(stack1, stack2) && ItemStack.areItemStackTagsEqual(stack1, stack2); + return stack1.isEmpty() == false && stack1.isItemEqual(stack2) && ItemStack.areNbtEqual(stack1, stack2); } private static boolean areSlotsInSameInventory(Slot slot1, Slot slot2) @@ -1834,7 +2082,7 @@ private static boolean areSlotsInSameInventory(Slot slot1, Slot slot2, boolean t { if (slot1.inventory == slot2.inventory) { - if (treatHotbarAsDifferent && slot1.inventory instanceof InventoryPlayer) + if (treatHotbarAsDifferent && slot1.inventory instanceof PlayerInventory) { int index1 = AccessorUtils.getSlotIndex(slot1); int index2 = AccessorUtils.getSlotIndex(slot2); @@ -1848,19 +2096,19 @@ private static boolean areSlotsInSameInventory(Slot slot1, Slot slot2, boolean t return false; } - private static ItemStack[] getOriginalStacks(Container container) + private static ItemStack[] getOriginalStacks(ScreenHandler container) { - ItemStack[] originalStacks = new ItemStack[container.inventorySlots.size()]; + ItemStack[] originalStacks = new ItemStack[container.slots.size()]; for (int i = 0; i < originalStacks.length; i++) { - originalStacks[i] = container.inventorySlots.get(i).getStack().copy(); + originalStacks[i] = container.slots.get(i).getStack().copy(); } return originalStacks; } - private static void restoreOriginalStacks(Container container, ItemStack[] originalStacks) + private static void restoreOriginalStacks(ScreenHandler container, ItemStack[] originalStacks) { for (int i = 0; i < originalStacks.length; i++) { @@ -1869,14 +2117,14 @@ private static void restoreOriginalStacks(Container container, ItemStack[] origi if (areStacksEqual(stackSlot, originalStacks[i]) == false || (isStackEmpty(stackSlot) == false && getStackSize(stackSlot) != getStackSize(originalStacks[i]))) { - container.putStackInSlot(i, originalStacks[i]); + container.getSlot(i).setStackNoCallbacks(originalStacks[i]); } } } - private static int getTargetSlot(Container container, ItemStack[] originalStacks) + private static int getTargetSlot(ScreenHandler container, ItemStack[] originalStacks) { - List slots = container.inventorySlots; + List slots = container.slots; for (int i = 0; i < originalStacks.length; i++) { @@ -1895,12 +2143,12 @@ private static int getTargetSlot(Container container, ItemStack[] originalStacks } /* - private void clickSlotsToMoveItems(Slot slot, GuiContainer gui, boolean matchingOnly, boolean toOtherInventory) + private void clickSlotsToMoveItems(Slot slot, ContainerScreen gui, boolean matchingOnly, boolean toOtherInventory) { - for (Slot slotTmp : gui.inventorySlots.inventorySlots) + for (Slot slotTmp : gui.getContainer().slots) { - if (slotTmp.slotNumber != slot.slotNumber && areSlotsInSameInventory(slotTmp, slot) == toOtherInventory && - slotTmp.getHasStack() && (matchingOnly == false || areStacksEqual(slot.getStack(), slotTmp.getStack()))) + if (slotTmp.id != slot.id && areSlotsInSameInventory(slotTmp, slot) == toOtherInventory && + slotTmp.hasStack() && (matchingOnly == false || areStacksEqual(slot.getStack(), slotTmp.getStack()))) { this.clickSlotsToMoveItemsFromSlot(slotTmp, gui, toOtherInventory); return; @@ -1915,47 +2163,48 @@ private void clickSlotsToMoveItems(Slot slot, GuiContainer gui, boolean matching } */ - private static void clickSlotsToMoveItemsFromSlot(Slot slotFrom, GuiContainer gui, boolean toOtherInventory) + private static void clickSlotsToMoveItemsFromSlot(Slot slotFrom, + HandledScreen gui, + boolean toOtherInventory) { - Minecraft mc = Minecraft.getMinecraft(); - EntityPlayer player = mc.player; // Left click to pick up the found source stack - leftClickSlot(gui, slotFrom.slotNumber); + leftClickSlot(gui, slotFrom.id); - if (isStackEmpty(player.inventory.getItemStack())) + if (isStackEmpty(gui.getScreenHandler().getCursorStack())) { return; } - for (Slot slotDst : gui.inventorySlots.inventorySlots) + for (Slot slotDst : gui.getScreenHandler().slots) { ItemStack stackDst = slotDst.getStack(); if (areSlotsInSameInventory(slotDst, slotFrom) != toOtherInventory && - (isStackEmpty(stackDst) || areStacksEqual(stackDst, player.inventory.getItemStack()))) + (isStackEmpty(stackDst) || areStacksEqual(stackDst, gui.getScreenHandler().getCursorStack()))) { // Left click to (try and) place items to the slot - leftClickSlot(gui, slotDst.slotNumber); + leftClickSlot(gui, slotDst.id); } - if (isStackEmpty(player.inventory.getItemStack())) + if (isStackEmpty(gui.getScreenHandler().getCursorStack())) { return; } } // Couldn't fit the entire stack to the target inventory, return the rest of the items - if (isStackEmpty(player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { - leftClickSlot(gui, slotFrom.slotNumber); + leftClickSlot(gui, slotFrom.id); } } - private static boolean clickSlotsToMoveSingleItem(GuiContainer gui, int slotFrom, int slotTo) + private static boolean clickSlotsToMoveSingleItem(HandledScreen gui, + int slotFrom, + int slotTo) { //System.out.println("clickSlotsToMoveSingleItem(from: " + slotFrom + ", to: " + slotTo + ")"); - Minecraft mc = Minecraft.getMinecraft(); - ItemStack stack = gui.inventorySlots.inventorySlots.get(slotFrom).getStack(); + ItemStack stack = gui.getScreenHandler().slots.get(slotFrom).getStack(); if (isStackEmpty(stack)) { @@ -1977,7 +2226,7 @@ private static boolean clickSlotsToMoveSingleItem(GuiContainer gui, int slotFrom rightClickSlot(gui, slotTo); // If there are items left in the cursor, then return them back to the original slot - if (isStackEmpty(mc.player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { // Left click again on the from-slot to return the rest of the items to it leftClickSlot(gui, slotFrom); @@ -1986,10 +2235,10 @@ private static boolean clickSlotsToMoveSingleItem(GuiContainer gui, int slotFrom return true; } - private static boolean clickSlotsToMoveSingleItemByShiftClick(GuiContainer gui, int slotFrom) + private static boolean clickSlotsToMoveSingleItemByShiftClick(HandledScreen gui, + int slotFrom) { - Minecraft mc = Minecraft.getMinecraft(); - Slot slot = gui.inventorySlots.inventorySlots.get(slotFrom); + Slot slot = gui.getScreenHandler().slots.get(slotFrom); ItemStack stack = slot.getStack(); if (isStackEmpty(stack)) @@ -2003,7 +2252,7 @@ private static boolean clickSlotsToMoveSingleItemByShiftClick(GuiContainer gui, leftClickSlot(gui, slotFrom); // Still items left in the slot, put the stack back and abort - if (slot.getHasStack()) + if (slot.hasStack()) { leftClickSlot(gui, slotFrom); return false; @@ -2018,7 +2267,7 @@ private static boolean clickSlotsToMoveSingleItemByShiftClick(GuiContainer gui, // ... and then shift-click on the slot shiftClickSlot(gui, slotFrom); - if (isStackEmpty(mc.player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { // ... and then return the rest of the items leftClickSlot(gui, slotFrom); @@ -2031,31 +2280,31 @@ private static boolean clickSlotsToMoveSingleItemByShiftClick(GuiContainer gui, * Try move items from slotFrom to slotTo * @return true if at least some items were moved */ - private static boolean clickSlotsToMoveItems(GuiContainer gui, int slotFrom, int slotTo) + private static boolean clickSlotsToMoveItems(HandledScreen gui, + int slotFrom, + int slotTo) { - Minecraft mc = Minecraft.getMinecraft(); - EntityPlayer player = mc.player; //System.out.println("clickSlotsToMoveItems(from: " + slotFrom + ", to: " + slotTo + ")"); // Left click to take items leftClickSlot(gui, slotFrom); // Couldn't take the items, bail out now - if (isStackEmpty(player.inventory.getItemStack())) + if (isStackEmpty(gui.getScreenHandler().getCursorStack())) { return false; } boolean ret = true; - int size = getStackSize(player.inventory.getItemStack()); + int size = getStackSize(gui.getScreenHandler().getCursorStack()); // Left click on the target slot to put the items to it leftClickSlot(gui, slotTo); // If there are items left in the cursor, then return them back to the original slot - if (isStackEmpty(player.inventory.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { - ret = getStackSize(player.inventory.getItemStack()) != size; + ret = getStackSize(gui.getScreenHandler().getCursorStack()) != size; // Left click again on the from-slot to return the rest of the items to it leftClickSlot(gui, slotFrom); @@ -2064,25 +2313,28 @@ private static boolean clickSlotsToMoveItems(GuiContainer gui, int slotFrom, int return ret; } - public static void dropStacksUntilEmpty(GuiContainer gui, int slotNum) + public static void dropStacksUntilEmpty(HandledScreen gui, + int slotNum) { - if (slotNum >= 0 && slotNum < gui.inventorySlots.inventorySlots.size()) + if (slotNum >= 0 && slotNum < gui.getScreenHandler().slots.size()) { - Slot slot = gui.inventorySlots.getSlot(slotNum); + Slot slot = gui.getScreenHandler().getSlot(slotNum); int failsafe = 64; - while (failsafe-- > 0 && slot.getHasStack()) + while (failsafe-- > 0 && slot.hasStack()) { dropStack(gui, slotNum); } } } - public static void dropStacksWhileHasItem(GuiContainer gui, int slotNum, ItemStack stackReference) + public static void dropStacksWhileHasItem(HandledScreen gui, + int slotNum, + ItemStack stackReference) { - if (slotNum >= 0 && slotNum < gui.inventorySlots.inventorySlots.size()) + if (slotNum >= 0 && slotNum < gui.getScreenHandler().slots.size()) { - Slot slot = gui.inventorySlots.getSlot(slotNum); + Slot slot = gui.getScreenHandler().getSlot(slotNum); int failsafe = 256; while (failsafe-- > 0 && areStacksEqual(slot.getStack(), stackReference)) @@ -2092,11 +2344,12 @@ public static void dropStacksWhileHasItem(GuiContainer gui, int slotNum, ItemSta } } - private static boolean shiftClickSlotWithCheck(GuiContainer gui, int slotNum) + private static boolean shiftClickSlotWithCheck(HandledScreen gui, + int slotNum) { - Slot slot = gui.inventorySlots.getSlot(slotNum); + Slot slot = gui.getScreenHandler().getSlot(slotNum); - if (slot == null || slot.getHasStack() == false) + if (slot == null || slot.hasStack() == false) { return false; } @@ -2104,20 +2357,21 @@ private static boolean shiftClickSlotWithCheck(GuiContainer gui, int slotNum) int sizeOrig = getStackSize(slot.getStack()); shiftClickSlot(gui, slotNum); - return slot.getHasStack() == false || getStackSize(slot.getStack()) != sizeOrig; + return slot.hasStack() == false || getStackSize(slot.getStack()) != sizeOrig; } - public static boolean tryMoveItemsVertically(GuiContainer gui, Slot slot, boolean moveUp, MoveAmount amount) + public static boolean tryMoveItemsVertically(HandledScreen gui, + Slot slot, + boolean moveUp, + MoveAmount amount) { - Minecraft mc = Minecraft.getMinecraft(); - // We require an empty cursor - if (slot == null || isStackEmpty(mc.player.inventory.getItemStack()) == false) + if (slot == null || isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { return false; } - List slots = getVerticallyFurthestSuitableSlotsForStackInSlot(gui.inventorySlots, slot, moveUp); + IntArrayList slots = getVerticallyFurthestSuitableSlotsForStackInSlot(gui.getScreenHandler(), slot, moveUp); if (slots.isEmpty()) { @@ -2144,61 +2398,64 @@ else if (amount == MoveAmount.ALL_MATCHING) return true; } - private static void moveMatchingStacksToSlots(GuiContainer gui, Slot slot, boolean moveUp) + private static void moveMatchingStacksToSlots(HandledScreen gui, + Slot slot, + boolean moveUp) { - List matchingSlots = getSlotNumbersOfMatchingStacks(gui.inventorySlots, slot, true, slot.getStack(), true, true, false); - List targetSlots = getSlotNumbersOfEmptySlots(gui.inventorySlots, slot, false, true, false); - targetSlots.addAll(getSlotNumbersOfEmptySlots(gui.inventorySlots, slot, true, true, false)); + IntArrayList matchingSlots = getSlotNumbersOfMatchingStacks(gui.getScreenHandler(), slot, true, slot.getStack(), true, true, false); + IntArrayList targetSlots = getSlotNumbersOfEmptySlots(gui.getScreenHandler(), slot, false, true, false); + targetSlots.addAll(getSlotNumbersOfEmptySlots(gui.getScreenHandler(), slot, true, true, false)); targetSlots.addAll(matchingSlots); - Collections.sort(matchingSlots, new SlotVerticalSorterSlotNumbers(gui.inventorySlots, ! moveUp)); - Collections.sort(targetSlots, new SlotVerticalSorterSlotNumbers(gui.inventorySlots, moveUp)); + matchingSlots.sort(new SlotVerticalSorterSlotNumbers(gui.getScreenHandler(), !moveUp)); + targetSlots.sort(new SlotVerticalSorterSlotNumbers(gui.getScreenHandler(), moveUp)); - for (int i = 0; i < matchingSlots.size(); ++i) + for (int matchingSlot : matchingSlots) { - int srcSlotNum = matchingSlots.get(i).intValue(); - Slot srcSlot = gui.inventorySlots.getSlot(srcSlotNum); + int srcSlotNum = matchingSlot; + Slot srcSlot = gui.getScreenHandler().getSlot(srcSlotNum); Slot lastSlot = moveStackToSlots(gui, srcSlot, targetSlots, false); - if (lastSlot == null || (lastSlot.slotNumber == srcSlot.slotNumber || (lastSlot.yPos > srcSlot.yPos) == moveUp)) + if (lastSlot == null || (lastSlot.id == srcSlot.id || (lastSlot.y > srcSlot.y) == moveUp)) { return; } } } - private static Slot moveStackToSlots(GuiContainer gui, Slot slotFrom, List slotsTo, boolean leaveOne) + private static Slot moveStackToSlots(HandledScreen gui, + Slot slotFrom, + IntArrayList slotsTo, + boolean leaveOne) { - Minecraft mc = Minecraft.getMinecraft(); - InventoryPlayer inv = mc.player.inventory; Slot lastSlot = null; // Empty slot, nothing to do - if (slotFrom.getHasStack() == false) + if (slotFrom.hasStack() == false) { return null; } // Pick up the stack - leftClickSlot(gui, slotFrom.slotNumber); + leftClickSlot(gui, slotFrom.id); if (leaveOne) { - rightClickSlot(gui, slotFrom.slotNumber); + rightClickSlot(gui, slotFrom.id); } for (int slotNum : slotsTo) { // Empty cursor, all done here - if (isStackEmpty(inv.getItemStack())) + if (isStackEmpty(gui.getScreenHandler().getCursorStack())) { break; } - Slot dstSlot = gui.inventorySlots.getSlot(slotNum); + Slot dstSlot = gui.getScreenHandler().getSlot(slotNum); - if (dstSlot.isItemValid(inv.getItemStack()) && - (dstSlot.getHasStack() == false || areStacksEqual(dstSlot.getStack(), inv.getItemStack()))) + if (dstSlot.canInsert(gui.getScreenHandler().getCursorStack()) && + (dstSlot.hasStack() == false || areStacksEqual(dstSlot.getStack(), gui.getScreenHandler().getCursorStack()))) { leftClickSlot(gui, slotNum); lastSlot = dstSlot; @@ -2206,33 +2463,32 @@ private static Slot moveStackToSlots(GuiContainer gui, Slot slotFrom, List slotsTo) + private static void moveOneItemToFirstValidSlot(HandledScreen gui, + Slot slotFrom, + IntArrayList slotsTo) { - Minecraft mc = Minecraft.getMinecraft(); - InventoryPlayer inv = mc.player.inventory; - // Pick up half of the the stack - rightClickSlot(gui, slotFrom.slotNumber); + rightClickSlot(gui, slotFrom.id); - if (isStackEmpty(inv.getItemStack())) + if (isStackEmpty(gui.getScreenHandler().getCursorStack())) { return; } - int sizeOrig = getStackSize(inv.getItemStack()); + int sizeOrig = getStackSize(gui.getScreenHandler().getCursorStack()); for (int slotNum : slotsTo) { rightClickSlot(gui, slotNum); - ItemStack stackCursor = inv.getItemStack(); + ItemStack stackCursor = gui.getScreenHandler().getCursorStack(); if (isStackEmpty(stackCursor) || getStackSize(stackCursor) != sizeOrig) { @@ -2241,81 +2497,85 @@ private static void moveOneItemToFirstValidSlot(GuiContainer gui, Slot slotFrom, } // Return the rest of the items, if any - if (isStackEmpty(inv.getItemStack()) == false) + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) { - leftClickSlot(gui, slotFrom.slotNumber); + leftClickSlot(gui, slotFrom.id); } } - private static List getVerticallyFurthestSuitableSlotsForStackInSlot(Container container, final Slot slotIn, boolean above) + private static IntArrayList getVerticallyFurthestSuitableSlotsForStackInSlot(ScreenHandler container, + Slot slotIn, + boolean above) { - if (slotIn == null || slotIn.getHasStack() == false) + if (slotIn == null || slotIn.hasStack() == false) { - return Collections.emptyList(); + return IntArrayList.of(); } - List slotNumbers = new ArrayList<>(); + IntArrayList slotNumbers = new IntArrayList(); ItemStack stackSlot = slotIn.getStack(); - for (Slot slotTmp : container.inventorySlots) + for (Slot slotTmp : container.slots) { - if (slotTmp.slotNumber != slotIn.slotNumber && slotTmp.yPos != slotIn.yPos) + if (slotTmp.id != slotIn.id && slotTmp.y != slotIn.y) { - if (above == slotTmp.yPos < slotIn.yPos) + if (above == slotTmp.y < slotIn.y) { ItemStack stackTmp = slotTmp.getStack(); - if ((isStackEmpty(stackTmp) && slotTmp.isItemValid(stackSlot)) || - (areStacksEqual(stackTmp, stackSlot)) && slotTmp.getItemStackLimit(stackTmp) > getStackSize(stackTmp)) + if ((isStackEmpty(stackTmp) && slotTmp.canInsert(stackSlot)) || + (areStacksEqual(stackTmp, stackSlot)) && slotTmp.getMaxItemCount(stackTmp) > getStackSize(stackTmp)) { - slotNumbers.add(slotTmp.slotNumber); + slotNumbers.add(slotTmp.id); } } } } - Collections.sort(slotNumbers, new SlotVerticalSorterSlotNumbers(container, above)); + slotNumbers.sort(new SlotVerticalSorterSlotNumbers(container, above)); return slotNumbers; } - public static void tryClearCursor(GuiContainer gui, Minecraft mc) + public static void tryClearCursor(HandledScreen gui) { - if (mc.player.inventory.getCurrentItem().isEmpty() == false) + ItemStack stackCursor = gui.getScreenHandler().getCursorStack(); + + if (isStackEmpty(stackCursor) == false) { - List emptySlots = getSlotNumbersOfEmptySlotsInPlayerInventory(gui.inventorySlots, false); + IntArrayList emptySlots = getSlotNumbersOfEmptySlotsInPlayerInventory(gui.getScreenHandler(), false); if (emptySlots.isEmpty() == false) { - leftClickSlot(gui, emptySlots.get(0)); + leftClickSlot(gui, emptySlots.getInt(0)); } else { - List matchingSlots = getSlotNumbersOfMatchingStacks(gui.inventorySlots, mc.player.inventory.getCurrentItem(), true); + IntArrayList matchingSlots = getSlotNumbersOfMatchingStacks(gui.getScreenHandler(), stackCursor, true); if (matchingSlots.isEmpty() == false) { for (int slotNum : matchingSlots) { - Slot slot = gui.inventorySlots.getSlot(slotNum); + Slot slot = gui.getScreenHandler().getSlot(slotNum); ItemStack stackSlot = slot.getStack(); - ItemStack stackCursor = mc.player.inventory.getCurrentItem(); if (slot == null || areStacksEqual(stackSlot, stackCursor) == false || - stackSlot.getCount() >= stackCursor.getMaxStackSize()) + getStackSize(stackSlot) >= stackCursor.getMaxCount()) { break; } leftClickSlot(gui, slotNum); + stackCursor = gui.getScreenHandler().getCursorStack(); } } } - } - if (mc.player.inventory.getCurrentItem().isEmpty() == false) - { - dropItemsFromCursor(gui); + if (isStackEmpty(gui.getScreenHandler().getCursorStack()) == false) + { + dropItemsFromCursor(gui); + } } } @@ -2344,7 +2604,7 @@ public int compare(Slot slot1, Slot slot2) { if (slot1.yPos == slot2.yPos) { - return (slot1.slotNumber < slot2.slotNumber) == this.topToBottom ? -1 : 1; + return (slot1.id < slot2.id) == this.topToBottom ? -1 : 1; } return (slot1.yPos < slot2.yPos) == this.topToBottom ? -1 : 1; @@ -2352,55 +2612,67 @@ public int compare(Slot slot1, Slot slot2) } */ - private static class SlotVerticalSorterSlotNumbers implements Comparator + private static class SlotVerticalSorterSlotNumbers implements IntComparator { - private final Container container; + private final ScreenHandler container; private final boolean topToBottom; - public SlotVerticalSorterSlotNumbers(Container container, boolean topToBottom) + public SlotVerticalSorterSlotNumbers(ScreenHandler container, boolean topToBottom) { this.container = container; this.topToBottom = topToBottom; } @Override - public int compare(Integer slotNum1, Integer slotNum2) + public int compare(int slotNum1, int slotNum2) { - Slot slot1 = this.container.getSlot(slotNum1.intValue()); - Slot slot2 = this.container.getSlot(slotNum2.intValue()); + if (Objects.equals(slotNum1, slotNum2)) + { + return 0; + } - if (slot1.yPos == slot2.yPos) + Slot slot1 = this.container.getSlot(slotNum1); + Slot slot2 = this.container.getSlot(slotNum2); + + if (slot1.y == slot2.y) { - return (slot1.slotNumber < slot2.slotNumber) == this.topToBottom ? -1 : 1; + return (slot1.id < slot2.id) == this.topToBottom ? -1 : 1; } - return (slot1.yPos < slot2.yPos) == this.topToBottom ? -1 : 1; + return (slot1.y < slot2.y) == this.topToBottom ? -1 : 1; } } - public static void clickSlot(GuiContainer gui, int slotNum, int mouseButton, ClickType type) + public static void clickSlot(HandledScreen gui, + int slotNum, + int mouseButton, + SlotActionType type) { - if (slotNum >= 0 && slotNum < gui.inventorySlots.inventorySlots.size()) + if (slotNum >= 0 && slotNum < gui.getScreenHandler().slots.size()) { - Slot slot = gui.inventorySlots.getSlot(slotNum); + Slot slot = gui.getScreenHandler().getSlot(slotNum); clickSlot(gui, slot, slotNum, mouseButton, type); } else { try { - Minecraft mc = Minecraft.getMinecraft(); - mc.playerController.windowClick(gui.inventorySlots.windowId, slotNum, mouseButton, type, mc.player); + MinecraftClient mc = MinecraftClient.getInstance(); + mc.interactionManager.clickSlot(gui.getScreenHandler().syncId, slotNum, mouseButton, type, mc.player); } catch (Exception e) { - LiteModItemScroller.logger.warn("Exception while emulating a slot click: gui: '{}', slotNum: {}, mouseButton; {}, ClickType: {}", + ItemScroller.logger.warn("Exception while emulating a slot click: gui: '{}', slotNum: {}, mouseButton; {}, SlotActionType: {}", gui.getClass().getName(), slotNum, mouseButton, type, e); } } } - public static void clickSlot(GuiContainer gui, Slot slot, int slotNum, int mouseButton, ClickType type) + public static void clickSlot(HandledScreen gui, + Slot slot, + int slotNum, + int mouseButton, + SlotActionType type) { try { @@ -2408,67 +2680,67 @@ public static void clickSlot(GuiContainer gui, Slot slot, int slotNum, int mouse } catch (Exception e) { - LiteModItemScroller.logger.warn("Exception while emulating a slot click: gui: '{}', slotNum: {}, mouseButton; {}, ClickType: {}", + ItemScroller.logger.warn("Exception while emulating a slot click: gui: '{}', slotNum: {}, mouseButton; {}, SlotActionType: {}", gui.getClass().getName(), slotNum, mouseButton, type, e); } } - public static void leftClickSlot(GuiContainer gui, Slot slot, int slotNumber) + public static void leftClickSlot(HandledScreen gui, Slot slot, int slotNumber) { - clickSlot(gui, slot, slotNumber, 0, ClickType.PICKUP); + clickSlot(gui, slot, slotNumber, 0, SlotActionType.PICKUP); } - public static void rightClickSlot(GuiContainer gui, Slot slot, int slotNumber) + public static void rightClickSlot(HandledScreen gui, Slot slot, int slotNumber) { - clickSlot(gui, slot, slotNumber, 1, ClickType.PICKUP); + clickSlot(gui, slot, slotNumber, 1, SlotActionType.PICKUP); } - public static void shiftClickSlot(GuiContainer gui, Slot slot, int slotNumber) + public static void shiftClickSlot(HandledScreen gui, Slot slot, int slotNumber) { - clickSlot(gui, slot, slotNumber, 0, ClickType.QUICK_MOVE); + clickSlot(gui, slot, slotNumber, 0, SlotActionType.QUICK_MOVE); } - public static void leftClickSlot(GuiContainer gui, int slotNum) + public static void leftClickSlot(HandledScreen gui, int slotNum) { - clickSlot(gui, slotNum, 0, ClickType.PICKUP); + clickSlot(gui, slotNum, 0, SlotActionType.PICKUP); } - public static void rightClickSlot(GuiContainer gui, int slotNum) + public static void rightClickSlot(HandledScreen gui, int slotNum) { - clickSlot(gui, slotNum, 1, ClickType.PICKUP); + clickSlot(gui, slotNum, 1, SlotActionType.PICKUP); } - public static void shiftClickSlot(GuiContainer gui, int slotNum) + public static void shiftClickSlot(HandledScreen gui, int slotNum) { - clickSlot(gui, slotNum, 0, ClickType.QUICK_MOVE); + clickSlot(gui, slotNum, 0, SlotActionType.QUICK_MOVE); } - public static void dropItemsFromCursor(GuiContainer gui) + public static void dropItemsFromCursor(HandledScreen gui) { - clickSlot(gui, -999, 0, ClickType.PICKUP); + clickSlot(gui, -999, 0, SlotActionType.PICKUP); } - public static void dropItem(GuiContainer gui, int slotNum) + public static void dropItem(HandledScreen gui, int slotNum) { - clickSlot(gui, slotNum, 0, ClickType.THROW); + clickSlot(gui, slotNum, 0, SlotActionType.THROW); } - public static void dropStack(GuiContainer gui, int slotNum) + public static void dropStack(HandledScreen gui, int slotNum) { - clickSlot(gui, slotNum, 1, ClickType.THROW); + clickSlot(gui, slotNum, 1, SlotActionType.THROW); } - public static void swapSlots(GuiContainer gui, int slotNum, int otherSlot) + public static void swapSlots(HandledScreen gui, int slotNum, int otherSlot) { - clickSlot(gui, slotNum, 0, ClickType.SWAP); - clickSlot(gui, otherSlot, 0, ClickType.SWAP); - clickSlot(gui, slotNum, 0, ClickType.SWAP); + clickSlot(gui, slotNum, 0, SlotActionType.SWAP); + clickSlot(gui, otherSlot, 0, SlotActionType.SWAP); + clickSlot(gui, slotNum, 0, SlotActionType.SWAP); } - private static void dragSplitItemsIntoSlots(GuiContainer gui, List targetSlots) + private static void dragSplitItemsIntoSlots(HandledScreen gui, + IntArrayList targetSlots) { - Minecraft mc = Minecraft.getMinecraft(); - ItemStack stackInCursor = mc.player.inventory.getItemStack(); + ItemStack stackInCursor = gui.getScreenHandler().getCursorStack(); if (isStackEmpty(stackInCursor)) { @@ -2477,30 +2749,27 @@ private static void dragSplitItemsIntoSlots(GuiContainer gui, List targ if (targetSlots.size() == 1) { - leftClickSlot(gui, targetSlots.get(0)); + leftClickSlot(gui, targetSlots.getInt(0)); return; } - int numSlots = gui.inventorySlots.inventorySlots.size(); - int loops = targetSlots.size(); + int numSlots = gui.getScreenHandler().slots.size(); // Start the drag - clickSlot(gui, -999, 0, ClickType.QUICK_CRAFT); + clickSlot(gui, -999, 0, SlotActionType.QUICK_CRAFT); - for (int i = 0; i < loops; i++) + for (int slotNum : targetSlots) { - int slotNum = targetSlots.get(i); - if (slotNum >= numSlots) { break; } - clickSlot(gui, targetSlots.get(i), 1, ClickType.QUICK_CRAFT); + clickSlot(gui, slotNum, 1, SlotActionType.QUICK_CRAFT); } // End the drag - clickSlot(gui, -999, 2, ClickType.QUICK_CRAFT); + clickSlot(gui, -999, 2, SlotActionType.QUICK_CRAFT); } /************************************************************** diff --git a/src/main/java/fi/dy/masa/itemscroller/util/ItemType.java b/src/main/java/fi/dy/masa/itemscroller/util/ItemType.java index 5ed0c527a..5ed50b57e 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/ItemType.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/ItemType.java @@ -1,11 +1,10 @@ package fi.dy.masa.itemscroller.util; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import net.minecraft.item.ItemStack; +import it.unimi.dsi.fastutil.ints.IntArrayList; /** * Wrapper class for ItemStack, which implements equals() @@ -31,9 +30,8 @@ public int hashCode() final int prime = 31; int result = 1; //result = prime * result + ((stack == null) ? 0 : stack.hashCode()); - result = prime * result + this.stack.getMetadata(); result = prime * result + this.stack.getItem().hashCode(); - result = prime * result + (this.stack.getTagCompound() != null ? this.stack.getTagCompound().hashCode() : 0); + result = prime * result + (this.stack.getNbt() != null ? this.stack.getNbt().hashCode() : 0); return result; } @@ -44,32 +42,24 @@ public boolean equals(Object obj) return true; if (obj == null) return false; - if (getClass() != obj.getClass()) + if (this.getClass() != obj.getClass()) return false; ItemType other = (ItemType) obj; if (InventoryUtils.isStackEmpty(this.stack) || InventoryUtils.isStackEmpty(other.stack)) { - if (InventoryUtils.isStackEmpty(this.stack) != InventoryUtils.isStackEmpty(other.stack)) - return false; + return InventoryUtils.isStackEmpty(this.stack) == InventoryUtils.isStackEmpty(other.stack); } else { - if (this.stack.getMetadata() != other.stack.getMetadata()) - { - return false; - } - if (this.stack.getItem() != other.stack.getItem()) { return false; } - return ItemStack.areItemStackTagsEqual(this.stack, other.stack); + return ItemStack.areNbtEqual(this.stack, other.stack); } - - return true; } /** @@ -77,9 +67,9 @@ public boolean equals(Object obj) * @param stacks * @return */ - public static Map> getSlotsPerItem(ItemStack[] stacks) + public static Map getSlotsPerItem(ItemStack[] stacks) { - Map> mapSlots = new HashMap>(); + Map mapSlots = new HashMap<>(); for (int i = 0; i < stacks.length; i++) { @@ -88,13 +78,7 @@ public static Map> getSlotsPerItem(ItemStack[] stacks) if (InventoryUtils.isStackEmpty(stack) == false) { ItemType item = new ItemType(stack); - List slots = mapSlots.get(item); - - if (slots == null) - { - slots = new ArrayList(); - mapSlots.put(item, slots); - } + IntArrayList slots = mapSlots.computeIfAbsent(item, k -> new IntArrayList()); slots.add(i); } diff --git a/src/main/java/fi/dy/masa/itemscroller/villager/FavoriteData.java b/src/main/java/fi/dy/masa/itemscroller/villager/FavoriteData.java new file mode 100644 index 000000000..49f3510a1 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/villager/FavoriteData.java @@ -0,0 +1,15 @@ +package fi.dy.masa.itemscroller.villager; + +import it.unimi.dsi.fastutil.ints.IntArrayList; + +public class FavoriteData +{ + public final IntArrayList favorites; + public final boolean isGlobal; + + public FavoriteData(IntArrayList favorites, boolean isGlobal) + { + this.favorites = favorites; + this.isGlobal = isGlobal; + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/villager/IMerchantScreenHandler.java b/src/main/java/fi/dy/masa/itemscroller/villager/IMerchantScreenHandler.java new file mode 100644 index 000000000..fb09b3934 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/villager/IMerchantScreenHandler.java @@ -0,0 +1,8 @@ +package fi.dy.masa.itemscroller.villager; + +import net.minecraft.village.TradeOfferList; + +public interface IMerchantScreenHandler +{ + TradeOfferList getOriginalList(); +} diff --git a/src/main/java/fi/dy/masa/itemscroller/villager/TradeType.java b/src/main/java/fi/dy/masa/itemscroller/villager/TradeType.java new file mode 100644 index 000000000..26256824f --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/villager/TradeType.java @@ -0,0 +1,122 @@ +package fi.dy.masa.itemscroller.villager; + +import javax.annotation.Nullable; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; +import net.minecraft.village.TradeOffer; + +public class TradeType +{ + public final Item buyItem1; + public final Item buyItem2; + public final Item sellItem; + + public TradeType(Item buyItem1, Item buyItem2, Item sellItem) + { + this.buyItem1 = buyItem1; + this.buyItem2 = buyItem2; + this.sellItem = sellItem; + } + + public boolean matchesTrade(TradeOffer trade) + { + ItemStack stackBuyItem1 = trade.getOriginalFirstBuyItem(); + ItemStack stackBuyItem2 = trade.getSecondBuyItem(); + ItemStack stackSellItem = trade.getSellItem(); + Item buyItem1 = stackBuyItem1.getItem(); + Item buyItem2 = stackBuyItem2.getItem(); + Item sellItem = stackSellItem.getItem(); + + return this.buyItem1 == buyItem1 && this.buyItem2 == buyItem2 && this.sellItem == sellItem; + } + + public NbtCompound toTag() + { + NbtCompound tag = new NbtCompound(); + + tag.putString("Buy1", getNameForItem(this.buyItem1)); + tag.putString("Buy2", getNameForItem(this.buyItem2)); + tag.putString("Sell", getNameForItem(this.sellItem)); + + return tag; + } + + @Nullable + public static TradeType fromTag(NbtCompound tag) + { + Item buy1 = getItemForName(tag.getString("Buy1")); + Item buy2 = getItemForName(tag.getString("Buy2")); + Item sell = getItemForName(tag.getString("Sell")); + + if (buy1 != Items.AIR || buy2 != Items.AIR || sell != Items.AIR) + { + return new TradeType(buy1, buy2, sell); + } + + return null; + } + + public static Item getItemForName(String name) + { + try + { + Identifier id = new Identifier(name); + return Registries.ITEM.get(id); + } + catch (Exception e) + { + return Items.AIR; + } + } + + public static String getNameForItem(Item item) + { + try + { + return Registries.ITEM.getId(item).toString(); + } + catch (Exception e) + { + return "?"; + } + } + + @Override + public boolean equals(Object o) + { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + TradeType tradeType = (TradeType) o; + + if (!buyItem1.equals(tradeType.buyItem1)) { return false; } + if (!buyItem2.equals(tradeType.buyItem2)) { return false; } + return sellItem.equals(tradeType.sellItem); + } + + @Override + public int hashCode() + { + int result = buyItem1.hashCode(); + result = 31 * result + buyItem2.hashCode(); + result = 31 * result + sellItem.hashCode(); + return result; + } + + public static TradeType of(TradeOffer trade) + { + ItemStack stackBuyItem1 = trade.getOriginalFirstBuyItem(); + ItemStack stackBuyItem2 = trade.getSecondBuyItem(); + ItemStack stackSellItem = trade.getSellItem(); + Item buyItem1 = stackBuyItem1.getItem(); + Item buyItem2 = stackBuyItem2.getItem(); + Item sellItem = stackSellItem.getItem(); + + return new TradeType(buyItem1, buyItem2, sellItem); + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/villager/VillagerData.java b/src/main/java/fi/dy/masa/itemscroller/villager/VillagerData.java index c90b092a4..093624d62 100644 --- a/src/main/java/fi/dy/masa/itemscroller/villager/VillagerData.java +++ b/src/main/java/fi/dy/masa/itemscroller/villager/VillagerData.java @@ -1,20 +1,18 @@ package fi.dy.masa.itemscroller.villager; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; import javax.annotation.Nullable; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtInt; +import net.minecraft.nbt.NbtList; import fi.dy.masa.itemscroller.util.Constants; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagInt; -import net.minecraft.nbt.NBTTagList; +import it.unimi.dsi.fastutil.ints.IntArrayList; public class VillagerData { private final UUID uuid; - private List favorites = new ArrayList<>(); + private final IntArrayList favorites = new IntArrayList(); private int tradeListPosition; - private int lastPage; VillagerData(UUID uuid) { @@ -36,21 +34,11 @@ void setTradeListPosition(int position) this.tradeListPosition = position; } - public int getLastPage() - { - return this.lastPage; - } - - void setLastPage(int page) - { - this.lastPage = page; - } - void toggleFavorite(int tradeIndex) { if (this.favorites.contains(tradeIndex)) { - this.favorites.remove(Integer.valueOf(tradeIndex)); + this.favorites.rem(tradeIndex); } else { @@ -58,49 +46,46 @@ void toggleFavorite(int tradeIndex) } } - public List getFavorites() + IntArrayList getFavorites() { return this.favorites; } - public NBTTagCompound toNBT() + public NbtCompound toNBT() { - NBTTagCompound tag = new NBTTagCompound(); + NbtCompound tag = new NbtCompound(); - tag.setLong("UUIDM", this.uuid.getMostSignificantBits()); - tag.setLong("UUIDL", this.uuid.getLeastSignificantBits()); - tag.setInteger("ListPosition", this.tradeListPosition); - tag.setInteger("CurrentPage", this.lastPage); + tag.putLong("UUIDM", this.uuid.getMostSignificantBits()); + tag.putLong("UUIDL", this.uuid.getLeastSignificantBits()); + tag.putInt("ListPosition", this.tradeListPosition); - NBTTagList tagList = new NBTTagList(); + NbtList tagList = new NbtList(); for (Integer val : this.favorites) { - tagList.appendTag(new NBTTagInt(val.intValue())); + tagList.add(NbtInt.of(val)); } - tag.setTag("Favorites", tagList); + tag.put("Favorites", tagList); return tag; } @Nullable - public static VillagerData fromNBT(NBTTagCompound tag) + public static VillagerData fromNBT(NbtCompound tag) { - if (tag.hasKey("UUIDM", Constants.NBT.TAG_LONG) && tag.hasKey("UUIDL", Constants.NBT.TAG_LONG)) + if (tag.contains("UUIDM", Constants.NBT.TAG_LONG) && tag.contains("UUIDL", Constants.NBT.TAG_LONG)) { VillagerData data = new VillagerData(new UUID(tag.getLong("UUIDM"), tag.getLong("UUIDL"))); + NbtList tagList = tag.getList("Favorites", Constants.NBT.TAG_INT); + final int count = tagList.size(); - data.tradeListPosition = tag.getInteger("ListPosition"); - data.lastPage = tag.getInteger("CurrentPage"); - NBTTagList tagList = tag.getTagList("Favorites", Constants.NBT.TAG_INT); - - final int count = tagList.tagCount(); data.favorites.clear(); + data.tradeListPosition = tag.getInt("ListPosition"); for (int i = 0; i < count; ++i) { - data.favorites.add(tagList.getIntAt(i)); + data.favorites.add(tagList.getInt(i)); } return data; diff --git a/src/main/java/fi/dy/masa/itemscroller/villager/VillagerDataStorage.java b/src/main/java/fi/dy/masa/itemscroller/villager/VillagerDataStorage.java index 440f6b901..f190c02cc 100644 --- a/src/main/java/fi/dy/masa/itemscroller/villager/VillagerDataStorage.java +++ b/src/main/java/fi/dy/masa/itemscroller/villager/VillagerDataStorage.java @@ -3,25 +3,33 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import fi.dy.masa.itemscroller.LiteModItemScroller; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtList; +import net.minecraft.screen.MerchantScreenHandler; +import net.minecraft.village.TradeOffer; +import net.minecraft.village.TradeOfferList; +import fi.dy.masa.itemscroller.ItemScroller; import fi.dy.masa.itemscroller.Reference; +import fi.dy.masa.itemscroller.config.Configs; import fi.dy.masa.itemscroller.util.Constants; import fi.dy.masa.malilib.util.FileUtils; import fi.dy.masa.malilib.util.StringUtils; -import net.minecraft.nbt.CompressedStreamTools; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; +import it.unimi.dsi.fastutil.ints.IntArrayList; public class VillagerDataStorage { private static final VillagerDataStorage INSTANCE = new VillagerDataStorage(); private final Map data = new HashMap<>(); + private final List globalFavorites = new ArrayList<>(); private UUID lastInteractedUUID; private boolean dirty; @@ -35,11 +43,6 @@ public void setLastInteractedUUID(UUID uuid) this.lastInteractedUUID = uuid; } - public boolean hasInteractionTarget() - { - return this.lastInteractedUUID != null; - } - @Nullable public VillagerData getDataForLastInteractionTarget() { @@ -72,41 +75,69 @@ public void setTradeListPosition(int position) } } - public void setLastPage(int page) + public void toggleFavorite(int tradeIndex) { VillagerData data = this.getDataFor(this.lastInteractedUUID, true); if (data != null) { - data.setLastPage(page); + data.toggleFavorite(tradeIndex); this.dirty = true; } } - public void toggleFavorite(int tradeIndex) + public void toggleGlobalFavorite(TradeOffer trade) { - VillagerData data = this.getDataFor(this.lastInteractedUUID, true); + TradeType type = TradeType.of(trade); - if (data != null) + if (this.globalFavorites.contains(type)) { - data.toggleFavorite(tradeIndex); - this.dirty = true; + this.globalFavorites.remove(type); + } + else + { + this.globalFavorites.add(type); } + + this.dirty = true; + } + + public FavoriteData getFavoritesForCurrentVillager(MerchantScreenHandler handler) + { + return this.getFavoritesForCurrentVillager(((IMerchantScreenHandler) handler).getOriginalList()); } - private void readFromNBT(NBTTagCompound nbt) + public FavoriteData getFavoritesForCurrentVillager(TradeOfferList originalTrades) { - if (nbt == null || nbt.hasKey("VillagerData", Constants.NBT.TAG_LIST) == false) + VillagerData data = this.getDataFor(this.lastInteractedUUID, false); + IntArrayList favorites = data != null ? data.getFavorites() : null; + + if (favorites != null && favorites.isEmpty() == false) + { + return new FavoriteData(favorites, false); + } + + if (Configs.Generic.VILLAGER_TRADE_USE_GLOBAL_FAVORITES.getBooleanValue() && this.lastInteractedUUID != null) + { + return new FavoriteData(VillagerUtils.getGlobalFavoritesFor(originalTrades, this.globalFavorites), true); + } + + return new FavoriteData(IntArrayList.of(), favorites == null); + } + + private void readFromNBT(NbtCompound nbt) + { + if (nbt == null || nbt.contains("VillagerData", Constants.NBT.TAG_LIST) == false) { return; } - NBTTagList tagList = nbt.getTagList("VillagerData", Constants.NBT.TAG_COMPOUND); - final int count = tagList.tagCount(); + NbtList tagList = nbt.getList("VillagerData", Constants.NBT.TAG_COMPOUND); + int count = tagList.size(); for (int i = 0; i < count; i++) { - NBTTagCompound tag = tagList.getCompoundTagAt(i); + NbtCompound tag = tagList.getCompound(i); VillagerData data = VillagerData.fromNBT(tag); if (data != null) @@ -114,18 +145,39 @@ private void readFromNBT(NBTTagCompound nbt) this.data.put(data.getUUID(), data); } } + + tagList = nbt.getList("GlobalFavorites", Constants.NBT.TAG_COMPOUND); + count = tagList.size(); + + for (int i = 0; i < count; i++) + { + NbtCompound tag = tagList.getCompound(i); + TradeType type = TradeType.fromTag(tag); + + if (type != null) + { + this.globalFavorites.add(type); + } + } } - private NBTTagCompound writeToNBT(@Nonnull NBTTagCompound nbt) + private NbtCompound writeToNBT(@Nonnull NbtCompound nbt) { - NBTTagList tagList = new NBTTagList(); + NbtList favoriteListData = new NbtList(); + NbtList globalFavoriteData = new NbtList(); for (VillagerData data : this.data.values()) { - tagList.appendTag(data.toNBT()); + favoriteListData.add(data.toNBT()); } - nbt.setTag("VillagerData", tagList); + for (TradeType type : this.globalFavorites) + { + globalFavoriteData.add(type.toTag()); + } + + nbt.put("VillagerData", favoriteListData); + nbt.put("GlobalFavorites", globalFavoriteData); this.dirty = false; @@ -152,26 +204,23 @@ private File getSaveDir() public void readFromDisk() { this.data.clear(); + this.globalFavorites.clear(); try { File saveDir = this.getSaveDir(); + File file = new File(saveDir, this.getFileName()); - if (saveDir != null) + if (file.exists() && file.isFile() && file.canRead()) { - File file = new File(saveDir, this.getFileName()); - - if (file.exists() && file.isFile() && file.canRead()) - { - FileInputStream is = new FileInputStream(file); - this.readFromNBT(CompressedStreamTools.readCompressed(is)); - is.close(); - } + FileInputStream is = new FileInputStream(file); + this.readFromNBT(NbtIo.readCompressed(is)); + is.close(); } } catch (Exception e) { - LiteModItemScroller.logger.warn("Failed to read villager data from file", e); + ItemScroller.logger.warn("Failed to read villager data from file", e); } } @@ -183,24 +232,16 @@ public void writeToDisk() { File saveDir = this.getSaveDir(); - if (saveDir == null) + if (saveDir.exists() == false && saveDir.mkdirs() == false) { + ItemScroller.logger.warn("Failed to create the data storage directory '{}'", saveDir.getPath()); return; } - if (saveDir.exists() == false) - { - if (saveDir.mkdirs() == false) - { - LiteModItemScroller.logger.warn("Failed to create the data storage directory '{}'", saveDir.getPath()); - return; - } - } - File fileTmp = new File(saveDir, this.getFileName() + ".tmp"); File fileReal = new File(saveDir, this.getFileName()); FileOutputStream os = new FileOutputStream(fileTmp); - CompressedStreamTools.writeCompressed(this.writeToNBT(new NBTTagCompound()), os); + NbtIo.writeCompressed(this.writeToNBT(new NbtCompound()), os); os.close(); if (fileReal.exists()) @@ -213,7 +254,7 @@ public void writeToDisk() } catch (Exception e) { - LiteModItemScroller.logger.warn("Failed to write villager data to file!", e); + ItemScroller.logger.warn("Failed to write villager data to file!", e); } } } diff --git a/src/main/java/fi/dy/masa/itemscroller/villager/VillagerUtils.java b/src/main/java/fi/dy/masa/itemscroller/villager/VillagerUtils.java new file mode 100644 index 000000000..9015610f1 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/villager/VillagerUtils.java @@ -0,0 +1,150 @@ +package fi.dy.masa.itemscroller.villager; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.network.packet.c2s.play.SelectMerchantTradeC2SPacket; +import net.minecraft.screen.MerchantScreenHandler; +import net.minecraft.village.TradeOffer; +import net.minecraft.village.TradeOfferList; +import fi.dy.masa.malilib.util.GuiUtils; +import it.unimi.dsi.fastutil.ints.IntArrayList; + +public class VillagerUtils +{ + public static boolean switchToTradeByVisibleIndex(int visibleIndex) + { + Screen screen = GuiUtils.getCurrentScreen(); + + if (screen instanceof MerchantScreen) + { + MerchantScreen merchantScreen = (MerchantScreen) screen; + MerchantScreenHandler handler = merchantScreen.getScreenHandler(); + + int realIndex = getRealTradeIndexFor(visibleIndex, handler); + + if (realIndex >= 0) + { + // Use the real (server-side) index + handler.setRecipeIndex(realIndex); + + // Use the "visible index", since this will access the custom list + handler.switchTo(visibleIndex); + + // Use the real (server-side) index + MinecraftClient.getInstance().getNetworkHandler().sendPacket(new SelectMerchantTradeC2SPacket(realIndex)); + + return true; + } + } + + return false; + } + + public static int getRealTradeIndexFor(int visibleIndex, MerchantScreenHandler handler) + { + if (handler instanceof IMerchantScreenHandler) + { + TradeOfferList originalList = ((IMerchantScreenHandler) handler).getOriginalList(); + TradeOfferList customList = handler.getRecipes(); + + if (originalList != null && customList != null && + visibleIndex >= 0 && visibleIndex < customList.size()) + { + TradeOffer trade = customList.get(visibleIndex); + + if (trade != null) + { + int realIndex = originalList.indexOf(trade); + + if (realIndex >= 0 && realIndex < originalList.size()) + { + return realIndex; + } + } + } + } + + return -1; + } + + public static TradeOfferList buildCustomTradeList(TradeOfferList originalList) + { + FavoriteData data = VillagerDataStorage.getInstance().getFavoritesForCurrentVillager(originalList); + IntArrayList favorites = data.favorites; + + //System.out.printf("build - fav: %s (%s), or: %d\n", favorites, data.isGlobal, originalList.size()); + + // Some favorites defined + if (favorites.isEmpty() == false) + { + TradeOfferList list = new TradeOfferList(); + int originalListSize = originalList.size(); + + // First pick all the favorited recipes, in the order they are in the favorites list + for (int index : favorites) + { + if (index >= 0 && index < originalListSize) + { + list.add(originalList.get(index)); + } + } + + // Then add the rest of the recipes in their original order + for (int i = 0; i < originalListSize; ++i) + { + if (favorites.contains(i) == false) + { + list.add(originalList.get(i)); + } + } + + return list; + } + + return originalList; + } + + public static IntArrayList getGlobalFavoritesFor(TradeOfferList originalTrades, Collection globalFavorites) + { + IntArrayList favorites = new IntArrayList(); + Map trades = new HashMap<>(); + final int size = originalTrades.size(); + + // Build a map from the trade types to the indices in the current villager's trade list + for (int i = 0; i < size; ++i) + { + TradeOffer trade = originalTrades.get(i); + trades.put(TradeType.of(trade), i); + } + + // Pick the trade list indices that are in the global favorites, in the order that they were global favorited + for (TradeType type : globalFavorites) + { + Integer index = trades.get(type); + + if (index != null) + { + favorites.add(index.intValue()); + } + } + + /* This is a version that is not sorted based on the order of the global favorites + for (int i = 0; i < size; ++i) + { + TradeType type = TradeType.of(originalTrades.get(i)); + + if (globalFavorites.contains(type)) + { + favorites.add(i); + } + } + */ + //System.out.printf("getGlobalFavoritesFor - list: %s - or: %d | global: %s\n", favorites, originalTrades.size(), globalFavorites); + + return favorites; + } +} diff --git a/src/main/resources/assets/itemscroller/icon.png b/src/main/resources/assets/itemscroller/icon.png new file mode 100644 index 000000000..1ee02a2ea Binary files /dev/null and b/src/main/resources/assets/itemscroller/icon.png differ diff --git a/src/main/resources/assets/itemscroller/lang/en_us.json b/src/main/resources/assets/itemscroller/lang/en_us.json new file mode 100644 index 000000000..7247a1551 --- /dev/null +++ b/src/main/resources/assets/itemscroller/lang/en_us.json @@ -0,0 +1,14 @@ +{ + "itemscroller.gui.button.config_gui.generic": "Generic", + "itemscroller.gui.button.config_gui.hotkeys": "Hotkeys", + "itemscroller.gui.button.config_gui.toggles": "Toggles", + + "itemscroller.gui.label.recipe_page": "Page %d / %d", + "itemscroller.gui.label.trades": "Trades", + "itemscroller.gui.label.trade_uses": "Trade uses: %d / %d", + + "itemscroller.gui.title.configs": "Item Scroller Configs", + + "itemscroller.message.toggled_mod_off": "Toggled all Item Scroller functionality §cOFF", + "itemscroller.message.toggled_mod_on": "Toggled all Item Scroller functionality §aON" +} \ No newline at end of file diff --git a/src/main/resources/assets/itemscroller/lang/en_us.lang b/src/main/resources/assets/itemscroller/lang/en_us.lang deleted file mode 100644 index 26b3d2be7..000000000 --- a/src/main/resources/assets/itemscroller/lang/en_us.lang +++ /dev/null @@ -1,13 +0,0 @@ -language.name=English -language.region=US -language.code=en_US - -itemscroller.gui.button.config_gui.generic=Generic -itemscroller.gui.button.config_gui.hotkeys=Hotkeys -itemscroller.gui.button.config_gui.toggles=Toggles - -itemscroller.gui.label.recipe_page=Page %d / %d -itemscroller.gui.label.trades=Trades -itemscroller.gui.label.trade_uses=Trade uses: %d / %d - -itemscroller.gui.title.configs=Item Scroller Configs diff --git a/src/main/resources/assets/itemscroller/textures/gui/gui_widgets.png b/src/main/resources/assets/itemscroller/textures/gui/gui_widgets.png index b522c0ae9..33c8662ba 100644 Binary files a/src/main/resources/assets/itemscroller/textures/gui/gui_widgets.png and b/src/main/resources/assets/itemscroller/textures/gui/gui_widgets.png differ diff --git a/src/main/resources/assets/itemscroller/textures/xcf/gui_widgets.xcf b/src/main/resources/assets/itemscroller/textures/xcf/gui_widgets.xcf index 4a11ad733..13442e4e0 100644 Binary files a/src/main/resources/assets/itemscroller/textures/xcf/gui_widgets.xcf and b/src/main/resources/assets/itemscroller/textures/xcf/gui_widgets.xcf differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 000000000..96c0ea95f --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,39 @@ +{ + "schemaVersion": 1, + "id": "itemscroller", + "name": "Item Scroller", + "version": "${mod_version}", + + "description": "Move items in inventory GUIs by scrolling the mouse wheel or dragging over slots", + "authors": [ + "masa" + ], + "contact": { + "homepage": "https://www.curseforge.com/minecraft/mc-mods/item-scroller", + "issues": "https://github.com/maruohon/itemscroller/issues", + "sources": "https://github.com/maruohon/itemscroller", + "twitter": "https://twitter.com/maruohon", + "discord": "https://discordapp.com/channels/211786369951989762/453662800460644354/" + }, + + "license": "LGPLv3", + "icon": "assets/itemscroller/icon.png", + "environment": "client", + "entrypoints": { + "main": [ + "fi.dy.masa.itemscroller.ItemScroller" + ], + "modmenu": [ + "fi.dy.masa.itemscroller.compat.modmenu.ModMenuImpl" + ] + }, + + "mixins": [ + "mixins.itemscroller.json" + ], + + "depends": { + "minecraft": "1.19.4", + "malilib": "0.15.x" + } +} diff --git a/src/main/resources/litemod.json b/src/main/resources/litemod.json deleted file mode 100644 index efe652388..000000000 --- a/src/main/resources/litemod.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "itemscroller", - "mcversion": "1.12", - "mixinConfigs": [ "mixins.itemscroller.json" ] -} \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info deleted file mode 100644 index d4143bfd3..000000000 --- a/src/main/resources/mcmod.info +++ /dev/null @@ -1,16 +0,0 @@ -[ -{ - "modid": "itemscroller", - "name": "Item Scroller", - "description": "Move items in inventory GUIs by scrolling the mouse wheel over slots with items in them", - "version": "${mod_version}", - "mcversion": "${minecraft_version}", - "url": "http://minecraft.curseforge.com/projects/item-scroller", - "updateUrl": "", - "authorList": ["masa"], - "credits": "ChickenBones (original idea, in NEI)", - "logoFile": "", - "screenshots": [], - "dependencies": [] -} -] diff --git a/src/main/resources/mixins.itemscroller.json b/src/main/resources/mixins.itemscroller.json index a4618e60a..8c1fe548f 100644 --- a/src/main/resources/mixins.itemscroller.json +++ b/src/main/resources/mixins.itemscroller.json @@ -1,18 +1,21 @@ { - "required": true, - "package": "fi.dy.masa.itemscroller.mixin", - "refmap": "mixins.itemscroller.refmap.json", - "minVersion": "0.6", - "client": [ - "IMixinGuiContainer", - "MixinEntityRenderer", - "IMixinSlot", - "MixinContainer", - "MixinGuiMerchant", - "MixinGuiScreen", - "MixinInventoryEffectRenderer" - ], - "injectors": { - "defaultRequire": 1 - } -} \ No newline at end of file + "required": true, + "package": "fi.dy.masa.itemscroller.mixin", + "compatibilityLevel": "JAVA_17", + "minVersion": "0.8", + "client": [ + "IMixinCraftingResultSlot", + "IMixinMerchantScreen", + "IMixinScreenWithHandler", + "IMixinSlot", + "MixinAbstractInventoryScreen", + "MixinClientPlayerInteractionManager", + "MixinCraftingScreenHandler", + "MixinGameRenderer", + "MixinMerchantScreen", + "MixinMerchantScreenHandler" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta deleted file mode 100644 index b18c6d6cd..000000000 --- a/src/main/resources/pack.mcmeta +++ /dev/null @@ -1,7 +0,0 @@ -{ - "pack": - { - "pack_format": 3, - "description": "Item Scroller Resources" - } -} \ No newline at end of file diff --git a/update.json b/update.json deleted file mode 100644 index cf10a8f91..000000000 --- a/update.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "homepage": "http://minecraft.curseforge.com/projects/item-scroller", - "promos": { - "1.12-latest": "0.11.0", - "1.12-recommended": "0.11.0", - "1.11.2-latest": "0.11.0", - "1.11.2-recommended": "0.11.0", - "1.11-latest": "0.11.0", - "1.11-recommended": "0.11.0", - "1.10.2-latest": "0.11.0", - "1.10.2-recommended": "0.11.0", - "1.10-latest": "0.10.3", - "1.10-recommended": "0.10.3", - "1.9.4-latest": "0.10.3", - "1.9.4-recommended": "0.10.3", - "1.9-latest": "0.10.3", - "1.9-recommended": "0.10.3", - "1.8.9-latest": "0.5.1", - "1.8.9-recommended": "0.5.1" - }, - "1.12": { - "0.11.0": "Update to Minecraft 1.12" - }, - "1.11.2": { - "0.11.0": "Added a crafting scrolling mode with recipe memory\nAdded right click crafting one stack\nConfig category re-organizing\nInternal refactoring", - "0.10.3": "Fix a NPE crash in some weird cases or GUIs", - "0.10.2": "Fix a possible crash in some GUIs with the toggle-disable keybind\nFix Ctrl + Shift + drop key not using a copy of the reference stack", - "0.10.1": "Fix not using the empty ItemStack for the pickup detection initialization - fixes a NPE", - "0.10.0": "Add GUI and Slot blacklists.\nAdd Ctrl+Alt+Shift + I to print debug info.\nBlacklisted AE2 terminal slots and TiCo Crafting Station side inventory slots by default.", - "0.9.1": "Fix a conflict with JEI recipe hiding, again (it broke again in 0.9.0 I believe)\nUse the new-ish Forge-added getters in GuiContainer instead of reflection", - "0.9.0": "A bunch of bug fixes and slight improvements\nAdded Ctrl + Shift + Drop dropping all matching stacks\nAdded a key bind to toggle Item Scroller on/off", - "0.8.1": "Update to Minecraft 1.11.2" - }, - "1.11": { - "0.11.0": "Added a crafting scrolling mode with recipe memory\nAdded right click crafting one stack\nConfig category re-organizing\nInternal refactoring", - "0.10.3": "Fix a NPE crash in some weird cases or GUIs", - "0.10.2": "Fix a possible crash in some GUIs with the toggle-disable keybind\nFix Ctrl + Shift + drop key not using a copy of the reference stack", - "0.10.1": "Fix not using the empty ItemStack for the pickup detection initialization - fixes a NPE", - "0.10.0": "Add GUI and Slot blacklists.\nAdd Ctrl+Alt+Shift + I to print debug info.\nBlacklisted AE2 terminal slots and TiCo Crafting Station side inventory slots by default.", - "0.9.1": "Fix a conflict with JEI recipe hiding, again (it broke again in 0.9.0 I believe)\nUse the new-ish Forge-added getters in GuiContainer instead of reflection", - "0.9.0": "A bunch of bug fixes and slight improvements\nAdded Ctrl + Shift + Drop dropping all matching stacks\nAdded a key bind to toggle Item Scroller on/off", - "0.8.0": "Add better checks for drag handler, to only cancel the event when necessary (fixes a compat. issue)\nAdd a fallback handler for shift/ctrl + scrolling", - "0.7.2": "Update to Minecraft 1.11" - }, - "1.10.2": { - "0.11.0": "Added a crafting scrolling mode with recipe memory\nAdded right click crafting one stack\nConfig category re-organizing\nInternal refactoring", - "0.10.3": "Fix a NPE crash in some weird cases or GUIs", - "0.10.2": "Fix a possible crash in some GUIs with the toggle-disable keybind\nFix Ctrl + Shift + drop key not using a copy of the reference stack", - "0.10.0": "Add GUI and Slot blacklists.\nAdd Ctrl+Alt+Shift + I to print debug info.\nBlacklisted AE2 terminal slots and TiCo Crafting Station side inventory slots by default.", - "0.9.1": "Fix a conflict with JEI recipe hiding, again (it broke again in 0.9.0 I believe)", - "0.9.0": "A bunch of bug fixes and slight improvements\nAdded Ctrl + Shift + Drop dropping all matching stacks\nAdded a key bind to toggle Item Scroller on/off", - "0.8.0": "Add better checks for drag handler, to only cancel the event when necessary (fixes a compat. issue)\nAdd a fallback handler for shift/ctrl + scrolling", - "0.7.1": "Ignore the creative inventory, fix a possible crash", - "0.7.0": "Change single item scrolling code, fixes some incompatibilities (like a crash with Chisel)\nAdd an option for slot position aware scroll direction (\"natural scroll direction\")", - "0.6.1": "Fix drag moving being broken in some inventories that use wrapped slots, for example Tinkers' Construct\nChange the code of the \"move everything except the last item\" drag mode to not need an empty slot in the source inventory", - "0.6.0": "Add shift + placing and shift + dropping to move/drop all matching items", - "0.5.2": "Don't cancel the input event when it doesn't need to be canceled, fixes a conflict with JEI item hiding", - "0.5.1": "Update to Minecraft 1.10.2" - }, - "1.10": { - "0.10.3": "Fix a NPE crash in some weird cases or GUIs", - "0.10.2": "Fix a possible crash in some GUIs with the toggle-disable keybind\nFix Ctrl + Shift + drop key not using a copy of the reference stack", - "0.10.0": "Add GUI and Slot blacklists.\nAdd Ctrl+Alt+Shift + I to print debug info.\nBlacklisted AE2 terminal slots and TiCo Crafting Station side inventory slots by default.", - "0.9.1": "Fix a conflict with JEI recipe hiding, again (it broke again in 0.9.0 I believe)", - "0.9.0": "A bunch of bug fixes and slight improvements\nAdded Ctrl + Shift + Drop dropping all matching stacks\nAdded a key bind to toggle Item Scroller on/off", - "0.8.0": "Add better checks for drag handler, to only cancel the event when necessary (fixes a compat. issue)\nAdd a fallback handler for shift/ctrl + scrolling", - "0.7.1": "Ignore the creative inventory, fix a possible crash", - "0.7.0": "Change single item scrolling code, fixes some incompatibilities (like a crash with Chisel)\nAdd an option for slot position aware scroll direction (\"natural scroll direction\")", - "0.6.1": "Fix drag moving being broken in some inventories that use wrapped slots, for example Tinkers' Construct\nChange the code of the \"move everything except the last item\" drag mode to not need an empty slot in the source inventory", - "0.6.0": "Add shift + placing and shift + dropping to move/drop all matching items", - "0.5.2": "Don't cancel the input event when it doesn't need to be canceled, fixes a conflict with JEI item hiding", - "0.5.1": "Update to Minecraft 1.10" - }, - "1.9.4": { - "0.10.3": "Fix a NPE crash in some weird cases or GUIs", - "0.10.2": "Fix a possible crash in some GUIs with the toggle-disable keybind\nFix Ctrl + Shift + drop key not using a copy of the reference stack", - "0.10.0": "Add GUI and Slot blacklists.\nAdd Ctrl+Alt+Shift + I to print debug info.\nBlacklisted AE2 terminal slots and TiCo Crafting Station side inventory slots by default.", - "0.9.1": "Fix a conflict with JEI recipe hiding, again (it broke again in 0.9.0 I believe)", - "0.9.0": "A bunch of bug fixes and slight improvements\nAdded Ctrl + Shift + Drop dropping all matching stacks\nAdded a key bind to toggle Item Scroller on/off", - "0.8.0": "Add better checks for drag handler, to only cancel the event when necessary (fixes a compat. issue)\nAdd a fallback handler for shift/ctrl + scrolling", - "0.7.1": "Ignore the creative inventory, fix a possible crash", - "0.7.0": "Change single item scrolling code, fixes some incompatibilities (like a crash with Chisel)\nAdd an option for slot position aware scroll direction (\"natural scroll direction\")", - "0.6.1": "Fix drag moving being broken in some inventories that use wrapped slots, for example Tinkers' Construct\nChange the code of the \"move everything except the last item\" drag mode to not need an empty slot in the source inventory", - "0.6.0": "Add shift + placing and shift + dropping to move/drop all matching items", - "0.5.2": "Don't cancel the input event when it doesn't need to be canceled, fixes a conflict with JEI item hiding", - "0.5.1": "Prevent the normal shift + click action (= moving the entire stack) when clicking in a \"partial drag mode\"", - "0.5.0": "Ported the 0.5.0 version from Minecraft 1.9 to 1.9.4" - }, - "1.9": { - "0.10.3": "Fix a NPE crash in some weird cases or GUIs", - "0.10.2": "Fix a possible crash in some GUIs with the toggle-disable keybind\nFix Ctrl + Shift + drop key not using a copy of the reference stack", - "0.10.0": "Add GUI and Slot blacklists.\nAdd Ctrl+Alt+Shift + I to print debug info.\nBlacklisted AE2 terminal slots and TiCo Crafting Station side inventory slots by default.", - "0.9.1": "Fix a conflict with JEI recipe hiding, again (it broke again in 0.9.0 I believe)", - "0.9.0": "A bunch of bug fixes and slight improvements\nAdded Ctrl + Shift + Drop dropping all matching stacks\nAdded a key bind to toggle Item Scroller on/off", - "0.8.0": "Add better checks for drag handler, to only cancel the event when necessary (fixes a compat. issue)\nAdd a fallback handler for shift/ctrl + scrolling", - "0.7.1": "Ignore the creative inventory, fix a possible crash", - "0.7.0": "Change single item scrolling code, fixes some incompatibilities (like a crash with Chisel)\nAdd an option for slot position aware scroll direction (\"natural scroll direction\")", - "0.6.1": "Fix drag moving being broken in some inventories that use wrapped slots, for example Tinkers' Construct\nChange the code of the \"move everything except the last item\" drag mode to not need an empty slot in the source inventory", - "0.6.0": "Add shift + placing and shift + dropping to move/drop all matching items", - "0.5.2": "Don't cancel the input event when it doesn't need to be canceled, fixes a conflict with JEI item hiding", - "0.5.1": "Prevent the normal shift + click action (= moving the entire stack) when clicking in a \"partial drag mode\"", - "0.5.0": "Fix a bug where shift + clicking would move two stacks because of the drag feature (only noticeable if the target slot can hold more than one regular stack's worth of items)\nAdd Ctrl + click dragging to move only one item from all the dragged-over slots\nAdd separate configs options for all the different dragging modes\nRenamed the old dragging config option - thus generating a new config is recommended", - "0.4.3": "Add right click dragging\nFix left mouse button's state getting stuck if the inventory is closed while holding the button", - "0.4.2": "Fix some derps when trying to scroll to/from output-only slots, like Furnace output or crafting output", - "0.4.1": "Fix a minor glitch when scrolling the last item to the hovered stack in some inventories\nAdd Forge update checker support", - "0.4.0": "Add config options to enable/disable the new modes\nAdd hold shift + drag to move items (like Mouse Tweaks) - the movement is interpolated: can handle fast cursor movement\nAdd special handling for Villagers: hover over the output slot, hold shift and scroll down to fill the recipe inputs (then scroll up to move the output items as usual). So basically hold shift and scroll down/up repeatedly to trade fast & easy.", - "0.3.0": "Add new modifier: Ctrl + scroll to move all matching items\nAdd new modifier: Ctrl + Shift + scroll to move everything", - "0.2.3": "Don't try to do things on fake slots. Fixes a cross-mod interaction issue/crash.", - "0.2.2": "Update for Forge 1805+", - "0.2.1": "Ported the 0.2.1 version from Minecraft 1.8.9 to 1.9" - }, - "1.8.9": { - "0.5.1": "Prevent the normal shift + click action (= moving the entire stack) when clicking in a \"partial drag mode\"", - "0.5.0": "Fix a bug where shift + clicking would move two stacks because of the drag feature (only noticeable if the target slot can hold more than one regular stack's worth of items)\nAdd Ctrl + click dragging to move only one item from all the dragged-over slots\nAdd separate configs options for all the different dragging modes\nRenamed the old dragging config option - thus generating a new config is recommended", - "0.4.3": "Add right click dragging\nFix left mouse button's state getting stuck if the inventory is closed while holding the button", - "0.4.2": "Fix some derps when trying to scroll to/from output-only slots, like Furnace output or crafting output", - "0.4.1": "Fix a minor glitch when scrolling the last item to the hovered stack in some inventories\nAdd Forge update checker support", - "0.4.0": "Add config options to enable/disable the new modes\nAdd hold shift + drag to move items (like Mouse Tweaks) - the movement is interpolated: can handle fast cursor movement\nAdd special handling for Villagers: hover over the output slot, hold shift and scroll down to fill the recipe inputs (then scroll up to move the output items as usual). So basically hold shift and scroll down/up repeatedly to trade fast & easy.", - "0.3.0": "Add new modifier: Ctrl + scroll to move all matching items\nAdd new modifier: Ctrl + Shift + scroll to move everything", - "0.2.3": "Don't try to do things on fake slots. Fixes a cross-mod interaction issue/crash.", - "0.2.1": "Add (better) support for scrolling items to/from the player's hotbar\nAdd config options to individually disable the single item and full stacks modes", - "0.2.0": "Add configs for reversing the scrolling direction/behavior\nAdd in-game config GUI support\nAdd support for moving full stacks by holding shift\nAdd support for inventory slots that are or extend SlotItemHandler", - "0.1.0": "Initial release" - } -}